[Swift] Swift implementation 🎉🎉 (#5603)
* Implemented the swift version of Flatbuffers
Implemented serailzing, reading, and mutating data from object monster
Fixes mis-aligned pointer issue
Fixes issue when shared strings are removed from table
Adds swift enum, structs code gen
Fixed namespace issues + started implementing the table gen
Added Mutate function to the code generator
Generated linux test cases
Fixed an issue with bools, and structs readers in table writer
Swift docker image added
Updated the test cases, and removed a method parameters in swift
Fixed createVector api when called with scalars
Fixed issues with scalar arrays, and fixed the code gen namespaces, added sample_binary.swift
Cleaned up project
Added enum vectors, and their readers
Refactored code
Added swift into the support document
Added documentation in docs, and fixed a small issue with Data() not being returned correctly
Fixes Lowercase issue, and prevents generating lookups for deprecated keys
* Made all the required funcs to have const + removed unneeded code + fix lowercase func
* Removed transform from lowercased and moved it to function
* Fixes an issue with iOS allocation from read
* Refactored cpp code to be more readable
* casts position into int for position
* Fix enums issue, moves scalar writer code to use memcpy
* Removed c_str from struct function
* Fixed script to generate new objects when ran on travis ci: fix
* Handles deallocating space allocated for structs
* Updated the test cases to adhere to the fileprivate lookup, no mutation for unions, and updated the names of the vector functions
2020-01-09 20:12:10 +00:00
|
|
|
import Foundation
|
|
|
|
|
|
|
|
public final class ByteBuffer {
|
|
|
|
|
|
|
|
/// pointer to the start of the buffer object in memory
|
|
|
|
private var _memory: UnsafeMutableRawPointer
|
|
|
|
/// The size of the elements written to the buffer + their paddings
|
|
|
|
private var _writerSize: Int = 0
|
|
|
|
/// Capacity of UInt8 the buffer can hold
|
|
|
|
private var _capacity: Int
|
|
|
|
|
|
|
|
/// Aliginment of the current memory being written to the buffer
|
|
|
|
internal var alignment = 1
|
|
|
|
/// Current Index which is being used to write to the buffer, it is written from the end to the start of the buffer
|
|
|
|
internal var writerIndex: Int { return _capacity - _writerSize }
|
|
|
|
|
|
|
|
/// Reader is the position of the current Writer Index (capacity - size)
|
|
|
|
public var reader: Int { return writerIndex }
|
|
|
|
/// Current size of the buffer
|
|
|
|
public var size: UOffset { return UOffset(_writerSize) }
|
|
|
|
/// Public Pointer to the buffer object in memory. This should NOT be modified for any reason
|
|
|
|
public var memory: UnsafeMutableRawPointer { return _memory }
|
|
|
|
/// Current capacity for the buffer
|
|
|
|
public var capacity: Int { return _capacity }
|
|
|
|
|
|
|
|
/// Constructor that creates a Flatbuffer object from a UInt8
|
|
|
|
/// - Parameter bytes: Array of UInt8
|
|
|
|
public init(bytes: [UInt8]) {
|
|
|
|
let ptr = UnsafePointer(bytes)
|
|
|
|
_memory = UnsafeMutableRawPointer.allocate(byteCount: bytes.count, alignment: alignment)
|
|
|
|
_memory.copyMemory(from: ptr, byteCount: bytes.count)
|
|
|
|
_capacity = bytes.count
|
|
|
|
_writerSize = _capacity
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Constructor that creates a Flatbuffer from the Swift Data type object
|
|
|
|
/// - Parameter data: Swift data Object
|
|
|
|
public init(data: Data) {
|
|
|
|
let pointer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)
|
|
|
|
data.copyBytes(to: pointer, count: data.count)
|
|
|
|
_memory = UnsafeMutableRawPointer(pointer)
|
|
|
|
_capacity = data.count
|
|
|
|
_writerSize = _capacity
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Constructor that creates a Flatbuffer instance with a size
|
|
|
|
/// - Parameter size: Length of the buffer
|
|
|
|
init(initialSize size: Int) {
|
|
|
|
let size = size.convertToPowerofTwo
|
|
|
|
_memory = UnsafeMutableRawPointer.allocate(byteCount: size, alignment: alignment)
|
|
|
|
_memory.initializeMemory(as: UInt8.self, repeating: 0, count: size)
|
|
|
|
_capacity = size
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a copy of the existing flatbuffer, by copying it to a different memory.
|
|
|
|
/// - Parameters:
|
|
|
|
/// - memory: Current memory of the buffer
|
|
|
|
/// - count: count of bytes
|
|
|
|
/// - removeBytes: Removes a number of bytes from the current size
|
|
|
|
internal init(memory: UnsafeMutableRawPointer, count: Int, removing removeBytes: Int) {
|
|
|
|
_memory = UnsafeMutableRawPointer.allocate(byteCount: count, alignment: alignment)
|
|
|
|
_memory.copyMemory(from: memory, byteCount: count)
|
|
|
|
_capacity = count
|
|
|
|
_writerSize = removeBytes
|
|
|
|
}
|
|
|
|
|
|
|
|
deinit { _memory.deallocate() }
|
|
|
|
|
|
|
|
/// Fills the buffer with padding by adding to the writersize
|
|
|
|
/// - Parameter padding: Amount of padding between two to be serialized objects
|
|
|
|
func fill(padding: UInt32) {
|
2020-01-16 20:08:59 +00:00
|
|
|
ensureSpace(size: padding)
|
[Swift] Swift implementation 🎉🎉 (#5603)
* Implemented the swift version of Flatbuffers
Implemented serailzing, reading, and mutating data from object monster
Fixes mis-aligned pointer issue
Fixes issue when shared strings are removed from table
Adds swift enum, structs code gen
Fixed namespace issues + started implementing the table gen
Added Mutate function to the code generator
Generated linux test cases
Fixed an issue with bools, and structs readers in table writer
Swift docker image added
Updated the test cases, and removed a method parameters in swift
Fixed createVector api when called with scalars
Fixed issues with scalar arrays, and fixed the code gen namespaces, added sample_binary.swift
Cleaned up project
Added enum vectors, and their readers
Refactored code
Added swift into the support document
Added documentation in docs, and fixed a small issue with Data() not being returned correctly
Fixes Lowercase issue, and prevents generating lookups for deprecated keys
* Made all the required funcs to have const + removed unneeded code + fix lowercase func
* Removed transform from lowercased and moved it to function
* Fixes an issue with iOS allocation from read
* Refactored cpp code to be more readable
* casts position into int for position
* Fix enums issue, moves scalar writer code to use memcpy
* Removed c_str from struct function
* Fixed script to generate new objects when ran on travis ci: fix
* Handles deallocating space allocated for structs
* Updated the test cases to adhere to the fileprivate lookup, no mutation for unions, and updated the names of the vector functions
2020-01-09 20:12:10 +00:00
|
|
|
_writerSize += (MemoryLayout<UInt8>.size * Int(padding))
|
|
|
|
}
|
|
|
|
|
|
|
|
///Adds an array of type Scalar to the buffer memory
|
|
|
|
/// - Parameter elements: An array of Scalars
|
|
|
|
func push<T: Scalar>(elements: [T]) {
|
|
|
|
let size = elements.count * MemoryLayout<T>.size
|
2020-01-16 20:08:59 +00:00
|
|
|
ensureSpace(size: UInt32(size))
|
[Swift] Swift implementation 🎉🎉 (#5603)
* Implemented the swift version of Flatbuffers
Implemented serailzing, reading, and mutating data from object monster
Fixes mis-aligned pointer issue
Fixes issue when shared strings are removed from table
Adds swift enum, structs code gen
Fixed namespace issues + started implementing the table gen
Added Mutate function to the code generator
Generated linux test cases
Fixed an issue with bools, and structs readers in table writer
Swift docker image added
Updated the test cases, and removed a method parameters in swift
Fixed createVector api when called with scalars
Fixed issues with scalar arrays, and fixed the code gen namespaces, added sample_binary.swift
Cleaned up project
Added enum vectors, and their readers
Refactored code
Added swift into the support document
Added documentation in docs, and fixed a small issue with Data() not being returned correctly
Fixes Lowercase issue, and prevents generating lookups for deprecated keys
* Made all the required funcs to have const + removed unneeded code + fix lowercase func
* Removed transform from lowercased and moved it to function
* Fixes an issue with iOS allocation from read
* Refactored cpp code to be more readable
* casts position into int for position
* Fix enums issue, moves scalar writer code to use memcpy
* Removed c_str from struct function
* Fixed script to generate new objects when ran on travis ci: fix
* Handles deallocating space allocated for structs
* Updated the test cases to adhere to the fileprivate lookup, no mutation for unions, and updated the names of the vector functions
2020-01-09 20:12:10 +00:00
|
|
|
elements.lazy.reversed().forEach { (s) in
|
|
|
|
push(value: s, len: MemoryLayout.size(ofValue: s))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A custom type of structs that are padded according to the flatbuffer padding,
|
|
|
|
/// - Parameters:
|
|
|
|
/// - value: Pointer to the object in memory
|
|
|
|
/// - size: Size of Value being written to the buffer
|
|
|
|
func push(struct value: UnsafeMutableRawPointer, size: Int) {
|
2020-01-16 20:08:59 +00:00
|
|
|
ensureSpace(size: UInt32(size))
|
[Swift] Swift implementation 🎉🎉 (#5603)
* Implemented the swift version of Flatbuffers
Implemented serailzing, reading, and mutating data from object monster
Fixes mis-aligned pointer issue
Fixes issue when shared strings are removed from table
Adds swift enum, structs code gen
Fixed namespace issues + started implementing the table gen
Added Mutate function to the code generator
Generated linux test cases
Fixed an issue with bools, and structs readers in table writer
Swift docker image added
Updated the test cases, and removed a method parameters in swift
Fixed createVector api when called with scalars
Fixed issues with scalar arrays, and fixed the code gen namespaces, added sample_binary.swift
Cleaned up project
Added enum vectors, and their readers
Refactored code
Added swift into the support document
Added documentation in docs, and fixed a small issue with Data() not being returned correctly
Fixes Lowercase issue, and prevents generating lookups for deprecated keys
* Made all the required funcs to have const + removed unneeded code + fix lowercase func
* Removed transform from lowercased and moved it to function
* Fixes an issue with iOS allocation from read
* Refactored cpp code to be more readable
* casts position into int for position
* Fix enums issue, moves scalar writer code to use memcpy
* Removed c_str from struct function
* Fixed script to generate new objects when ran on travis ci: fix
* Handles deallocating space allocated for structs
* Updated the test cases to adhere to the fileprivate lookup, no mutation for unions, and updated the names of the vector functions
2020-01-09 20:12:10 +00:00
|
|
|
_memory.advanced(by: writerIndex - size).copyMemory(from: value, byteCount: size)
|
|
|
|
defer { value.deallocate() }
|
|
|
|
_writerSize += size
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Adds an object of type Scalar into the buffer
|
|
|
|
/// - Parameters:
|
|
|
|
/// - value: Object that will be written to the buffer
|
|
|
|
/// - len: Offset to subtract from the WriterIndex
|
|
|
|
func push<T: Scalar>(value: T, len: Int) {
|
2020-01-16 20:08:59 +00:00
|
|
|
ensureSpace(size: UInt32(len))
|
[Swift] Swift implementation 🎉🎉 (#5603)
* Implemented the swift version of Flatbuffers
Implemented serailzing, reading, and mutating data from object monster
Fixes mis-aligned pointer issue
Fixes issue when shared strings are removed from table
Adds swift enum, structs code gen
Fixed namespace issues + started implementing the table gen
Added Mutate function to the code generator
Generated linux test cases
Fixed an issue with bools, and structs readers in table writer
Swift docker image added
Updated the test cases, and removed a method parameters in swift
Fixed createVector api when called with scalars
Fixed issues with scalar arrays, and fixed the code gen namespaces, added sample_binary.swift
Cleaned up project
Added enum vectors, and their readers
Refactored code
Added swift into the support document
Added documentation in docs, and fixed a small issue with Data() not being returned correctly
Fixes Lowercase issue, and prevents generating lookups for deprecated keys
* Made all the required funcs to have const + removed unneeded code + fix lowercase func
* Removed transform from lowercased and moved it to function
* Fixes an issue with iOS allocation from read
* Refactored cpp code to be more readable
* casts position into int for position
* Fix enums issue, moves scalar writer code to use memcpy
* Removed c_str from struct function
* Fixed script to generate new objects when ran on travis ci: fix
* Handles deallocating space allocated for structs
* Updated the test cases to adhere to the fileprivate lookup, no mutation for unions, and updated the names of the vector functions
2020-01-09 20:12:10 +00:00
|
|
|
var v = value.convertedEndian
|
|
|
|
memcpy(_memory.advanced(by: writerIndex - len), &v, len)
|
|
|
|
_writerSize += len
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Adds a string to the buffer using swift.utf8 object
|
|
|
|
/// - Parameter str: String that will be added to the buffer
|
|
|
|
/// - Parameter len: length of the string
|
|
|
|
func push(string str: String, len: Int) {
|
2020-01-16 20:08:59 +00:00
|
|
|
ensureSpace(size: UInt32(len))
|
[Swift] Swift implementation 🎉🎉 (#5603)
* Implemented the swift version of Flatbuffers
Implemented serailzing, reading, and mutating data from object monster
Fixes mis-aligned pointer issue
Fixes issue when shared strings are removed from table
Adds swift enum, structs code gen
Fixed namespace issues + started implementing the table gen
Added Mutate function to the code generator
Generated linux test cases
Fixed an issue with bools, and structs readers in table writer
Swift docker image added
Updated the test cases, and removed a method parameters in swift
Fixed createVector api when called with scalars
Fixed issues with scalar arrays, and fixed the code gen namespaces, added sample_binary.swift
Cleaned up project
Added enum vectors, and their readers
Refactored code
Added swift into the support document
Added documentation in docs, and fixed a small issue with Data() not being returned correctly
Fixes Lowercase issue, and prevents generating lookups for deprecated keys
* Made all the required funcs to have const + removed unneeded code + fix lowercase func
* Removed transform from lowercased and moved it to function
* Fixes an issue with iOS allocation from read
* Refactored cpp code to be more readable
* casts position into int for position
* Fix enums issue, moves scalar writer code to use memcpy
* Removed c_str from struct function
* Fixed script to generate new objects when ran on travis ci: fix
* Handles deallocating space allocated for structs
* Updated the test cases to adhere to the fileprivate lookup, no mutation for unions, and updated the names of the vector functions
2020-01-09 20:12:10 +00:00
|
|
|
if str.utf8.withContiguousStorageIfAvailable({ self.push(bytes: $0, len: len) }) != nil {
|
|
|
|
} else {
|
|
|
|
let utf8View = str.utf8
|
|
|
|
for c in utf8View.lazy.reversed() {
|
|
|
|
push(value: c, len: 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Writes a string to Bytebuffer using UTF8View
|
|
|
|
/// - Parameters:
|
|
|
|
/// - bytes: Pointer to the view
|
|
|
|
/// - len: Size of string
|
|
|
|
private func push(bytes: UnsafeBufferPointer<String.UTF8View.Element>, len: Int) -> Bool {
|
|
|
|
_memory.advanced(by: writerIndex - len).copyMemory(from:
|
|
|
|
UnsafeRawPointer(bytes.baseAddress!), byteCount: len)
|
|
|
|
_writerSize += len
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Write stores an object into the buffer directly or indirectly.
|
|
|
|
///
|
|
|
|
/// Direct: ignores the capacity of buffer which would mean we are referring to the direct point in memory
|
|
|
|
/// indirect: takes into respect the current capacity of the buffer (capacity - index), writing to the buffer from the end
|
|
|
|
/// - Parameters:
|
|
|
|
/// - value: Value that needs to be written to the buffer
|
|
|
|
/// - index: index to write to
|
|
|
|
/// - direct: Should take into consideration the capacity of the buffer
|
|
|
|
func write<T>(value: T, index: Int, direct: Bool = false) {
|
|
|
|
var index = index
|
|
|
|
if !direct {
|
|
|
|
index = _capacity - index
|
|
|
|
}
|
|
|
|
_memory.storeBytes(of: value, toByteOffset: index, as: T.self)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Makes sure that buffer has enouch space for each of the objects that will be written into it
|
|
|
|
/// - Parameter size: size of object
|
|
|
|
@discardableResult
|
2020-01-16 20:08:59 +00:00
|
|
|
func ensureSpace(size: UInt32) -> UInt32 {
|
[Swift] Swift implementation 🎉🎉 (#5603)
* Implemented the swift version of Flatbuffers
Implemented serailzing, reading, and mutating data from object monster
Fixes mis-aligned pointer issue
Fixes issue when shared strings are removed from table
Adds swift enum, structs code gen
Fixed namespace issues + started implementing the table gen
Added Mutate function to the code generator
Generated linux test cases
Fixed an issue with bools, and structs readers in table writer
Swift docker image added
Updated the test cases, and removed a method parameters in swift
Fixed createVector api when called with scalars
Fixed issues with scalar arrays, and fixed the code gen namespaces, added sample_binary.swift
Cleaned up project
Added enum vectors, and their readers
Refactored code
Added swift into the support document
Added documentation in docs, and fixed a small issue with Data() not being returned correctly
Fixes Lowercase issue, and prevents generating lookups for deprecated keys
* Made all the required funcs to have const + removed unneeded code + fix lowercase func
* Removed transform from lowercased and moved it to function
* Fixes an issue with iOS allocation from read
* Refactored cpp code to be more readable
* casts position into int for position
* Fix enums issue, moves scalar writer code to use memcpy
* Removed c_str from struct function
* Fixed script to generate new objects when ran on travis ci: fix
* Handles deallocating space allocated for structs
* Updated the test cases to adhere to the fileprivate lookup, no mutation for unions, and updated the names of the vector functions
2020-01-09 20:12:10 +00:00
|
|
|
if Int(size) + _writerSize > _capacity { reallocate(size) }
|
|
|
|
assert(size < FlatBufferMaxSize, "Buffer can't grow beyond 2 Gigabytes")
|
|
|
|
return size
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reallocates the buffer incase the object to be written doesnt fit in the current buffer
|
|
|
|
/// - Parameter size: Size of the current object
|
2020-01-16 20:08:59 +00:00
|
|
|
fileprivate func reallocate(_ size: UInt32) {
|
[Swift] Swift implementation 🎉🎉 (#5603)
* Implemented the swift version of Flatbuffers
Implemented serailzing, reading, and mutating data from object monster
Fixes mis-aligned pointer issue
Fixes issue when shared strings are removed from table
Adds swift enum, structs code gen
Fixed namespace issues + started implementing the table gen
Added Mutate function to the code generator
Generated linux test cases
Fixed an issue with bools, and structs readers in table writer
Swift docker image added
Updated the test cases, and removed a method parameters in swift
Fixed createVector api when called with scalars
Fixed issues with scalar arrays, and fixed the code gen namespaces, added sample_binary.swift
Cleaned up project
Added enum vectors, and their readers
Refactored code
Added swift into the support document
Added documentation in docs, and fixed a small issue with Data() not being returned correctly
Fixes Lowercase issue, and prevents generating lookups for deprecated keys
* Made all the required funcs to have const + removed unneeded code + fix lowercase func
* Removed transform from lowercased and moved it to function
* Fixes an issue with iOS allocation from read
* Refactored cpp code to be more readable
* casts position into int for position
* Fix enums issue, moves scalar writer code to use memcpy
* Removed c_str from struct function
* Fixed script to generate new objects when ran on travis ci: fix
* Handles deallocating space allocated for structs
* Updated the test cases to adhere to the fileprivate lookup, no mutation for unions, and updated the names of the vector functions
2020-01-09 20:12:10 +00:00
|
|
|
let currentWritingIndex = writerIndex
|
|
|
|
while _capacity <= _writerSize + Int(size) {
|
|
|
|
_capacity = _capacity << 1
|
|
|
|
}
|
|
|
|
|
|
|
|
/// solution take from Apple-NIO
|
|
|
|
_capacity = _capacity.convertToPowerofTwo
|
|
|
|
|
|
|
|
let newData = UnsafeMutableRawPointer.allocate(byteCount: _capacity, alignment: alignment)
|
|
|
|
newData.initializeMemory(as: UInt8.self, repeating: 0, count: _capacity)
|
|
|
|
newData
|
|
|
|
.advanced(by: writerIndex)
|
|
|
|
.copyMemory(from: _memory.advanced(by: currentWritingIndex), byteCount: _writerSize)
|
|
|
|
_memory.deallocate()
|
|
|
|
_memory = newData
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Clears the current size of the buffer
|
|
|
|
public func clearSize() {
|
|
|
|
_writerSize = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Clears the current instance of the buffer, replacing it with new memory
|
|
|
|
public func clear() {
|
|
|
|
_writerSize = 0
|
|
|
|
alignment = 1
|
|
|
|
_memory.deallocate()
|
|
|
|
_memory = UnsafeMutableRawPointer.allocate(byteCount: _capacity, alignment: alignment)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Resizes the buffer size
|
|
|
|
/// - Parameter size: new size for the buffer
|
|
|
|
internal func resize(_ size: Int) {
|
|
|
|
_writerSize = size
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reads an object from the buffer
|
|
|
|
/// - Parameters:
|
|
|
|
/// - def: Type of the object
|
|
|
|
/// - position: the index of the object in the buffer
|
|
|
|
public func read<T>(def: T.Type, position: Int) -> T {
|
|
|
|
return _memory.advanced(by: position).load(as: T.self)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reads a slice from the memory assuming a type of T
|
|
|
|
/// - Parameters:
|
|
|
|
/// - index: index of the object to be read from the buffer
|
|
|
|
/// - count: count of bytes in memory
|
|
|
|
public func readSlice<T>(index: Int32,
|
|
|
|
count: Int32) -> [T] {
|
|
|
|
let start = _memory.advanced(by: Int(index)).assumingMemoryBound(to: T.self)
|
|
|
|
let array = UnsafeBufferPointer(start: start, count: Int(count))
|
|
|
|
return Array(array)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reads a string from the buffer and encodes it to a swift string
|
|
|
|
/// - Parameters:
|
|
|
|
/// - index: index of the string in the buffer
|
|
|
|
/// - count: length of the string
|
|
|
|
/// - type: Encoding of the string
|
|
|
|
public func readString(at index: Int32,
|
|
|
|
count: Int32,
|
|
|
|
type: String.Encoding = .utf8) -> String? {
|
|
|
|
let start = _memory.advanced(by: Int(index)).assumingMemoryBound(to: UInt8.self)
|
|
|
|
let bufprt = UnsafeBufferPointer(start: start, count: Int(count))
|
|
|
|
return String(bytes: Array(bufprt), encoding: type)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a new Flatbuffer object that's duplicated from the current one
|
|
|
|
/// - Parameter removeBytes: the amount of bytes to remove from the current Size
|
|
|
|
public func duplicate(removing removeBytes: Int = 0) -> ByteBuffer {
|
|
|
|
return ByteBuffer(memory: _memory, count: _capacity, removing: _writerSize - removeBytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
#if DEBUG
|
|
|
|
func debugMemory(str: String) {
|
|
|
|
let bufprt = UnsafeBufferPointer(start: _memory.assumingMemoryBound(to: UInt8.self),
|
|
|
|
count: _capacity)
|
|
|
|
let a = Array(bufprt)
|
|
|
|
print(str, a, " \nwith buffer size: \(a.count) and writer size: \(_writerSize)")
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|