diff --git a/.codecov.yml b/.codecov.yml
new file mode 100644
index 0000000..6b34001
--- /dev/null
+++ b/.codecov.yml
@@ -0,0 +1,5 @@
+ignore:
+ - "Source/library" # C library
+ - "Tests" # Test files
+
+max_report_age: off
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..2e628c5
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# C libarary
+Source/library/* linguist-vendored
\ No newline at end of file
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
new file mode 100644
index 0000000..e69de29
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..d2ed08a
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,38 @@
+
+
+
+## DO NOT Submit Non-bug Issue or Framework Question Here
+Please go to [pcpLiu/SerranoExplore](https://github.com/pcpLiu/SerranoExplore) opening an issue for general support. This repo only accepts bug-related issues.
+
+
+
+
+
+#### Description
+(Give a overall description of the situation you have met)
+
+
+#### Environment Information
+- Serrano version: (XXXXX)
+- Xcode Version: (XXXXX)
+- Platform: (macOS or iOS)
+- Platform System version: (iOSxxxx or macOSxxx)
+
+
+#### Reproduce Steps
+(A __gist__ or __github repo__ is prefered. Should be detailed enough to reproduce.)
+
+
+#### Expecting behavior
+(Describe what result or behavior should be expecting)
+
+
+#### Actual behavior
+(Describe actual result or behavior)
+
+
+#### Possible Implementation (Optional)
+(Optional implementation to fix this bug)
\ No newline at end of file
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..c596e8c
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,21 @@
+
+
+
+## DO NOT Submit Non-bug PR Here
+Please go to [pcpLiu/SerranoExplore](https://github.com/pcpLiu/SerranoExplore) to discuss feature enhancements. This repo only accepts bug-related PRs.
+
+
+
+
+
+#### Description
+(Description of this PR)
+
+#### Related Issues
+(List related issues for this PR)
+
+#### How Has This Been Tested?
+(Give information how your tested the fixed code)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5363363
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,13 @@
+serrano.xcodeproj/xcuserdata
+.testing
+xcuserdata
+
+*.coverage.txt
+*.xccoverage.plist
+
+.cov/
+build/
+DerivedData/
+
+# auto generated local docs
+docs/generated_docs/*
diff --git a/.jazzy.yaml b/.jazzy.yaml
new file mode 100644
index 0000000..840b4ca
--- /dev/null
+++ b/.jazzy.yaml
@@ -0,0 +1,39 @@
+## This configure used to generate guides docs only. Not for API
+
+
+xcodebuild_arguments: ['-scheme', 'TestingHostApp', '-project', 'serrano.xcodeproj']
+module: Serrano
+
+author: pcpLiu
+author_url: https://github.com/pcpLiu
+github_url: https://github.com/pcpLiu/Serrano
+module_version: v0.1.0-alpha
+github_file_prefix: https://github.com/pcpLiu/Serrano/tree/v0.1.0-alpha
+root_url: http://serrano-lib.org/docs/v0.1.0-alpha/guides
+theme: docs/fullwidth
+output: docs/generated_docs/v0.1.0-alpha/guides
+
+# Generate guides only
+exclude: Source/*
+
+
+documentation: docs/guides/**/*.md
+abstract: docs/sections/**/*.md'
+readme: docs/sections/Home.md
+
+
+# Layout
+custom_categories:
+ - name: 'Getting started'
+ children:
+ - 'What is Serrano'
+ - 'Core concepts'
+ - 'VGG16 Example'
+ - name: 'Extension'
+ children:
+ - 'Write your own operator'
+ - 'Write your own optimizer'
+ - name: 'Contribution'
+ children: [Contribution]
+ - name: 'API Reference'
+ children: [API Reference]
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..fd22b6d
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,13 @@
+os: osx
+osx_image: xcode9
+language: swift
+xcode_sdk:
+ - iphonesimulator10.3
+ - macosx10.12
+
+branches:
+ only:
+ - master
+
+script:
+ - bash scripts/travisCI.sh
\ No newline at end of file
diff --git a/Examples/Graph/VGG16.swift b/Examples/Graph/VGG16.swift
new file mode 100644
index 0000000..5c30f9d
--- /dev/null
+++ b/Examples/Graph/VGG16.swift
@@ -0,0 +1,168 @@
+import XCTest
+import Serrano
+
+/**
+This code shows how to construct a VGG16 network using graph's low-level API.
+
+[vgg16](http://book.paddlepaddle.org/03.image_classification/image/vgg16.png)
+*/
+func configureVGG16() -> ComputationGraph {
+ let g = ComputationGraph()
+
+ // input [244, 244, 3]
+ let shape = TensorShape(dataType: .float, shape: [244, 244, 3])
+ let input = g.tensor(shape: shape)
+
+ // block 1
+ let convOp = ConvOperator2D(numFilters: 64,
+ kernelSize: [3, 3],
+ padMode: PaddingMode.Same,
+ channelPosition: TensorChannelOrder.Last,
+ inputShape: input.shape)
+ let (out, _, _) = g.operation(inputs: [input], op: convOp)
+
+ let convOp1 = ConvOperator2D(numFilters: 64,
+ kernelSize: [3, 3],
+ padMode: PaddingMode.Same,
+ channelPosition: TensorChannelOrder.Last,
+ inputShape: out.first!.shape)
+ let (out1, _, _) = g.operation(inputs: out, op: convOp1)
+
+ let poo1 = MaxPool2DOperator(kernelSize: [2, 2],
+ channelPosition: TensorChannelOrder.Last,
+ paddingMode: PaddingMode.Valid)
+ let (out_block_1, _, _) = g.operation(inputs: out1, op: poo1)
+
+ // block 2
+ let convOp2 = ConvOperator2D(numFilters: 128,
+ kernelSize: [3, 3],
+ padMode: PaddingMode.Same,
+ channelPosition: TensorChannelOrder.Last,
+ inputShape: out_block_1.first!.shape)
+ let (out2, _, _) = g.operation(inputs: out_block_1, op: convOp2)
+
+ let convOp3 = ConvOperator2D(numFilters: 128,
+ kernelSize: [3, 3],
+ padMode: PaddingMode.Same,
+ channelPosition: TensorChannelOrder.Last,
+ inputShape: out2.first!.shape)
+ let (out3, _, _) = g.operation(inputs: out2, op: convOp3)
+
+ let poo2 = MaxPool2DOperator(kernelSize: [2, 2],
+ channelPosition: TensorChannelOrder.Last,
+ paddingMode: PaddingMode.Valid)
+ let (out_block_2, _, _) = g.operation(inputs: out3, op: poo2)
+
+ // block 3
+ let convOp4 = ConvOperator2D(numFilters: 256,
+ kernelSize: [3, 3],
+ padMode: PaddingMode.Same,
+ channelPosition: TensorChannelOrder.Last,
+ inputShape: out_block_2.first!.shape)
+ let (out4, _, _) = g.operation(inputs: out_block_2, op: convOp4)
+
+ let convOp5 = ConvOperator2D(numFilters: 256,
+ kernelSize: [3, 3],
+ padMode: PaddingMode.Same,
+ channelPosition: TensorChannelOrder.Last,
+ inputShape: out4.first!.shape)
+ let (out5, _, _) = g.operation(inputs: out4, op: convOp5)
+
+ let convOp6 = ConvOperator2D(numFilters: 256,
+ kernelSize: [3, 3],
+ padMode: PaddingMode.Same,
+ channelPosition: TensorChannelOrder.Last,
+ inputShape: out5.first!.shape)
+ let (out6, _, _) = g.operation(inputs: out5, op: convOp6)
+
+ let poo3 = MaxPool2DOperator(kernelSize: [2, 2],
+ channelPosition: TensorChannelOrder.Last,
+ paddingMode: PaddingMode.Valid)
+ let (out_block_3, _, _) = g.operation(inputs: out6, op: poo3)
+
+ // bloack 4
+ let convOp7 = ConvOperator2D(numFilters: 512,
+ kernelSize: [3, 3],
+ padMode: PaddingMode.Same,
+ channelPosition: TensorChannelOrder.Last,
+ inputShape: out_block_3.first!.shape)
+ let (out7, _, _) = g.operation(inputs: out_block_3, op: convOp7)
+
+ let convOp8 = ConvOperator2D(numFilters: 512,
+ kernelSize: [3, 3],
+ padMode: PaddingMode.Same,
+ channelPosition: TensorChannelOrder.Last,
+ inputShape: out7.first!.shape)
+ let (out8, _, _) = g.operation(inputs: out7, op: convOp8)
+
+ let convOp9 = ConvOperator2D(numFilters: 512,
+ kernelSize: [3, 3],
+ padMode: PaddingMode.Same,
+ channelPosition: TensorChannelOrder.Last,
+ inputShape: out8.first!.shape)
+ let (out9, _, _) = g.operation(inputs: out8, op: convOp9)
+
+ let poo4 = MaxPool2DOperator(kernelSize: [2, 2],
+ channelPosition: TensorChannelOrder.Last,
+ paddingMode: PaddingMode.Valid)
+ let (out_block_4, _, _) = g.operation(inputs: out9, op: poo4)
+
+ // block 5
+ let convOp10 = ConvOperator2D(numFilters: 512,
+ kernelSize: [3, 3],
+ padMode: PaddingMode.Same,
+ channelPosition: TensorChannelOrder.Last,
+ inputShape: out_block_4.first!.shape)
+ let (out10, _, _) = g.operation(inputs: out_block_4, op: convOp10)
+
+ let convOp11 = ConvOperator2D(numFilters: 512,
+ kernelSize: [3, 3],
+ padMode: PaddingMode.Same,
+ channelPosition: TensorChannelOrder.Last,
+ inputShape: out10.first!.shape)
+ let (out11, _, _) = g.operation(inputs: out10, op: convOp11)
+
+ let convOp12 = ConvOperator2D(numFilters: 512,
+ kernelSize: [3, 3],
+ padMode: PaddingMode.Same,
+ channelPosition: TensorChannelOrder.Last,
+ inputShape: out11.first!.shape)
+ let (out12, _, _) = g.operation(inputs: out11, op: convOp12)
+
+ let poo5 = MaxPool2DOperator(kernelSize: [2, 2],
+ channelPosition: TensorChannelOrder.Last,
+ paddingMode: PaddingMode.Valid)
+ let (out_block_5, _, _) = g.operation(inputs: out12, op: poo5)
+
+ // block 6
+ let fc13 = FullyconnectedOperator(inputDim: 25088, numUnits: 4096)
+ let (out13, _, _) = g.operation(inputs: out_block_5, op: fc13)
+
+ let fc14 = FullyconnectedOperator(inputDim: 4096, numUnits: 4096)
+ let (out14, _, _) = g.operation(inputs: out13, op: fc14)
+
+ let fc15 = FullyconnectedOperator(inputDim: 4096, numUnits: 4096)
+ let (out15, _, _) = g.operation(inputs: out14, op: fc15)
+
+ return g
+}
+
+
+
+class Example_VGG16: XCTestCase {
+ // Test vgg16 network forward
+ // Suggestion: run this function on macOS or real iOS devices supporting GPU. It goona be very slow on CPU mode.
+ func testVGG16Forawad() {
+ SerranoLogging.release = true
+
+ let _ = SerranoEngine.configuredEngine.configureEngine(computationMode: .GPU)
+ let vgg16 = configureVGG16()
+ vgg16.allocateAllTensors()
+ vgg16.forwardPrepare()
+
+ let start = CFAbsoluteTimeGetCurrent()
+// vgg16.forward(mode: .CPU)
+ vgg16.forward(mode: .GPU)
+ print("Forward Execution Time : \((CFAbsoluteTimeGetCurrent() - start) * 100) ms")
+ }
+}
diff --git a/Examples/Graph/YOLO.swift b/Examples/Graph/YOLO.swift
new file mode 100644
index 0000000..9f185f3
--- /dev/null
+++ b/Examples/Graph/YOLO.swift
@@ -0,0 +1,36 @@
+//
+// YOLO.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 10/25/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+
+class YOLO: XCTestCase {
+
+ override func setUp() {
+ super.setUp()
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ }
+
+ override func tearDown() {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ super.tearDown()
+ }
+
+ func testExample() {
+ // This is an example of a functional test case.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+ }
+
+ func testPerformanceExample() {
+ // This is an example of a performance test case.
+ self.measure {
+ // Put the code you want to measure the time of here.
+ }
+ }
+
+ //TODO:
+}
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..6b22c72
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Zhonghao LIU
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ab3da86
--- /dev/null
+++ b/README.md
@@ -0,0 +1,80 @@
+![logo](https://github.com/pcpLiu/Serrano/blob/master/logo.png)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Serrano
+Aiming to offering popular and cutting edge techs in deep learning area on iOS devices, Serrano is developed as a tool for developers & researchers with deep learning background to quickly implement their ideas on iOS devices. Meanwhile, it supports macOS as a pure swift framework bonus.
+
+## Features
+- Implemented an efficient NDArray class [Tensor](http://serrano-lib.org/docs/v0.1.0-alpha/api/Classes/Tensor.html) which supports:
+ - CPU calculation with [BLAS](https://developer.apple.com/documentation/accelerate/blas)/[vecLib](https://developer.apple.com/documentation/accelerate/veclib)/[vDSP](https://developer.apple.com/documentation/accelerate/vdsp) for better performance
+ - GPU calculation on [no-copy MTLBuffer](https://developer.apple.com/documentation/metal/mtldevice/1433382-makebuffer) for memory saving
+- Including common [operators](http://serrano-lib.org/docs/v0.1.0-alpha/api/Classes.html) for constructing various computation graphs and it is easy to [implement custom operators]().
+- A python package [serrano-tools](https://github.com/pcpLiu/serrano-tools) is here to help convert your existing models (Under development)
+- No third-party library dependent. Compatible from iOS 10.
+
+## Install
+
+#### Via CocoaPods
+Install the latest version:
+```
+pod 'Serrano', :git => 'https://github.com/pcpLiu/Serrano.git'
+```
+
+
+#### Manually integrate into your workspace/project
+
+Download or clone Serrano and drag `serrano.xcodeproj` into your workspace or project.
+Add `Serrano` into the `Target Dependencies` of your target.
+
+
+## Docs
+The APIs are hosted at [http://serrano-lib.org](http://serrano-lib.org/docs/v0.1.0-alpha/api/).
+
+Currently, we are adding more guides.
+
+## Questions && Issues
+ :bangbang: Please __only open [bug]/[feature request] related issues__ in THIS repo and follow this [issue guide](). :bangbang:
+
+__For any general issue/discussion || framework support__, please go to [pcpLiu/SerranoExplore](https://github.com/pcpLiu/SerranoExplore) opening an issue. Also you can discuss on [Gitter](https://gitter.im/SerranoFramework/Lobby)
+
+
+## macOSX
+Serrano was developed as an iOS framework. However, the framework could be added and used in Cocoa applications (macOS App) without effort.
+
+## Contribution
+Contribution are wanted! And please read the [Contributing Guide]() before making a PR.
+
+## License
+Serrano is liscensed under [MIT](https://github.com/pcpLiu/serrano/blob/master/LICENSE). Copyright (c) 2017, Zhonghao (Tim) Liu.
+
+
+## Acknowledgement
+Serrano are inspired and influenced by these open source projects:
+
+- [MXNET](https://github.com/apache/incubator-mxnet)
+- [Keras](https://github.com/fchollet/keras)
+- [TensorFlow](https://www.tensorflow.org/)
+- [Caffe](https://github.com/BVLC/caffe)
diff --git a/Serrano.podspec b/Serrano.podspec
new file mode 100644
index 0000000..67df39a
--- /dev/null
+++ b/Serrano.podspec
@@ -0,0 +1,26 @@
+Pod::Spec.new do |spec|
+ spec.name = 'Serrano'
+ spec.version = '0.1.1-alpha'
+ spec.license = { :type => 'MIT' }
+ spec.homepage = 'https://github.com/pcpLiu/Serrano'
+ spec.authors = { 'Tim Liu' => 'pcpliu.dev@gmail.com' }
+ spec.summary = 'Graph computation library for iOS'
+ spec.source = { :git => 'https://github.com/pcpLiu/Serrano.git', :tag => 'v0.1.1-alpha' }
+ spec.module_name = 'Serrano'
+ spec.documentation_url = 'http://serrano-lib.org'
+
+ spec.ios.deployment_target = '10.0'
+ spec.osx.deployment_target = '10.11'
+ spec.requires_arc = true
+
+ spec.framework = 'Accelerate'
+ spec.weak_framework = 'Metal'
+
+ spec.source_files = 'Source/**/*.{swift,c,h}'
+ spec.public_header_files = 'Source/SupportingFiles/Serrano.h'
+ spec.resources = 'Source/**/*.{metal}'
+
+ spec.pod_target_xcconfig = { 'SWIFT_VERSION' => '3.2', 'SWIFT_INCLUDE_PATHS' => '$(SRCROOT)/Serrano/Source/library/FBSUtil/**'}
+ spec.preserve_paths = 'Source/library/FBSUtil/module.modulemap'
+
+end
\ No newline at end of file
diff --git a/Source/Serrano/core/engine.swift b/Source/Serrano/core/engine.swift
new file mode 100644
index 0000000..5cdc678
--- /dev/null
+++ b/Source/Serrano/core/engine.swift
@@ -0,0 +1,238 @@
+//
+// engine.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 3/3/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import Metal
+
+//public enum SerranoBackend {
+// case Serrano
+// //case CoreML
+//}
+
+
+/**
+ This class is supposed be initialized and configured at the very beggining of your app.
+ It is responsible for setting up computation envirionment involving with iOS's GPU device.
+ It will initialize serveral instances which will involve with heavy GPU evaluation and are reconmmended reusing by [Apple documentation](https://developer.apple.com/library/content/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/index.html).
+
+ The `configuredEngine` is a _singleton_ instance and should be used everywhere you need to access `GPUDevice` (an instance of `MTLDevice`),\
+ `serranoCommandQueue` (an instance of `MTLCommandQueue`).
+
+ Initial and configure a GPU engine:
+
+ ````swift
+ let configuredEngine = SerranoEngine.configuredEngine
+ let (success, message) = configuredEngine.configureEngine(computationMode: .GPU, serranoCommandQueue: nil, serranoMTLLibrary: nil, systemDefaultGPUDevice: nil)
+ if !success {
+ // Failed to congiure GPU device
+ print(message)
+ }
+ ````
+
+ Initial and configure a CPU engine:
+ ````swift
+ let configuredEngine = SerranoEngine.configuredEngine
+ configuredEngine.configureEngine(computationMode: .CPU, serranoCommandQueue: nil, serranoMTLLibrary: nil, systemDefaultGPUDevice: nil)
+ ````
+ */
+public class SerranoEngine {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// Instance
+ public static let configuredEngine = SerranoEngine()
+
+ /// GPU device.
+ public var GPUDevice: MTLDevice?
+
+ /// Metal Command Queue for serrano.
+ public var serranoCommandQueue: MTLCommandQueue?
+
+ /// Engine computation mode
+ public var computationMode: OperatorComputationMode
+
+ /// Loaded MTLComputePipelineStates.
+ /// A dictionary with `label` as key and corresponding `MTLComputePipelineState` as value.
+ public var loadedGPUKernels: [String : MTLComputePipelineState]
+
+ /// METAL library
+ public var metalLibrary: MTLLibrary?
+
+// /// Backend compuation
+// public var backend: SerranoBackend = SerranoBackend.Serrano
+
+ /// Default operator computation Mode
+ public var defaultComputationMode: OperatorComputationMode = .Auto
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Mark: - Initializer
+
+ private init() {
+ self.GPUDevice = nil
+ self.serranoCommandQueue = nil
+ self.computationMode = .CPU
+ self.loadedGPUKernels = [:]
+ self.metalLibrary = nil
+ }
+
+ /**
+ Setup `GPUDevice`, `serranoCommandQueue` and `computationMode` of engine.
+
+ - Parameters:
+ - computationMode: one of choies in enum `OperatorComputationMode` (`GPU` or `CPU`).
+ - serranoCommandQueue: Optional. If it is `nil`, method will initialize a command queue in `GPU` mode.
+ - serranoMTLLibrary: Optional. If it is `nil`, method will initialize a default `MTLLibrary`.
+ - systemDefaultGPUDevice: Optional. If this is nil and `computationMode` is `GPU`,\
+ method will try to create a instance calling `MTLCreateSystemDefaultDevice()`.\
+ If failed to initialize a GPU device instance, will return `false`.
+
+ - Returns:
+ - result: If configure engine successfully.
+ - message: Message information of configure.
+
+ - Note: When user gives no device instance and method fails to initialized a GPU device instance, \
+ `Serrano` will automatically set up `computationMode` to `OperatorComputationMode.CPU`.
+
+ - Warning: This method must be called before doing any GPU related computation.
+
+ */
+ public func configureEngine(computationMode mode: OperatorComputationMode,
+ serranoCommandQueue commandQueue: MTLCommandQueue? = nil,
+ serranoMTLLibrary library: MTLLibrary? = nil,
+ systemDefaultGPUDevice gpu: MTLDevice? = nil) -> (result: Bool, message: String){
+ if mode == .GPU {
+ // device
+ if gpu == nil {
+ self.GPUDevice = MTLCreateSystemDefaultDevice()
+
+ guard (self.GPUDevice != nil) else {
+ self.computationMode = .CPU
+ SerranoLogging.warningLogging(message: "Failed to create a MTLDevice instance from MTLCreateSystemDefaultDevice().",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return (false, "Failed to create a MTLDevice instance from MTLCreateSystemDefaultDevice().")
+ }
+ } else {
+ self.GPUDevice = gpu
+ }
+
+ // computeCommandQueue
+ if commandQueue == nil {
+ self.serranoCommandQueue = self.GPUDevice!.makeCommandQueue()
+ } else {
+ self.serranoCommandQueue = commandQueue
+ }
+
+ // default library
+ if library == nil {
+ // build directly
+ var libpath = Bundle(for: type(of: self)).path(forResource: "default", ofType: "metallib")
+ if libpath == nil {
+ // build through cocoapod
+ libpath = Bundle(identifier: "SerranoMetalLib")?.path(forResource: "default", ofType: "metallib")
+ }
+ guard libpath != nil else {
+ return (false, "Failed to locate default.metlib")
+ }
+ do {
+ try self.metalLibrary = self.GPUDevice!.makeLibrary(filepath: libpath!)
+ } catch {
+ return (false, "Failed to create the default metal library. Erro:\n\(error)")
+ }
+ } else {
+ library!.label = "serranoMetalLibrary"
+ self.metalLibrary = library!
+ }
+
+ self.computationMode = .GPU
+ return (true, "Setup engine computation mode to \(self.computationMode) with device: \(self.GPUDevice!.description).")
+ } else {
+ self.computationMode = .CPU
+ return (true, "Setup engine computation mode to \(self.computationMode).")
+ }
+
+
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Mark: - Methods
+
+ /**
+ Reset all attributes of engine to default values.
+ */
+ public func resetEngine() {
+ self.GPUDevice = nil
+ self.computationMode = .CPU
+ self.loadedGPUKernels = [:]
+ self.metalLibrary = nil
+ self.serranoCommandQueue = nil
+ }
+
+ /**
+ Check if current configured engine has available GPU device.
+
+ - Returns: `true` if has available device.
+ */
+ public func hasAvailableGPU() -> Bool {
+ return self.GPUDevice != nil
+ }
+
+ /**
+ Get a kernel in `loadedGPUKernels`, if not in loaded kernels, return `nil`
+
+ - Parameters
+ - label: label of target `MTLComputePipelineState`
+ */
+ internal func getGPUKernelFromLoadedKernels(kenelLabel label: String) -> MTLComputePipelineState? {
+ return self.loadedGPUKernels[label]
+ }
+
+ /**
+ Load GPU compute kernel from Serrano's default Metal library.
+ Before loading method will check if already loaded this kernel, if loaded just return the kernel
+ If not found in `loadedGPUKernels`, method will create a new `MTLComputePipelineState` instance for function with target `label` and return the kernel.
+ When failed to find the function, will return a `nil` kernel with error information.
+
+ - Parameters:
+ - label: Kernel function name in Metal file.
+
+ - Returns: `kernel`: Optional. Target kernel. Will be `nil` if fails to load; `message`: message information.
+
+ - Note: User must configure the engine first before loading any GPU kernels. If method could find `GPUDevice` or 'metalLibrary' is `nil`, it will
+ raise a fatal error.
+ */
+ public func loadGPUKernel(kernelLabel label: String) -> (result: MTLComputePipelineState?, message: String){
+ guard self.GPUDevice != nil && self.metalLibrary != nil else {
+ fatalError("[Serrano]Trying to load GPU kernel without initializing a GPU device or Metal Library.")
+ }
+
+ var kernel: MTLComputePipelineState? = self.getGPUKernelFromLoadedKernels(kenelLabel: label)
+ if kernel != nil {
+ return (kernel!, "Successfully loaded GPU kernel \(label).")
+ }
+
+ let function = self.metalLibrary!.makeFunction(name: label)
+ if function == nil {
+ return (nil, "Could not find the Metal kernel function: \(label).")
+ }
+
+
+ do {
+ try kernel = self.GPUDevice!.makeComputePipelineState(function: function!)
+ }
+ catch {
+ return (nil, "Error catched when tring to load kernel \(label). Details:\n\(error)")
+ }
+
+
+ self.loadedGPUKernels[label] = kernel!
+ return (kernel!, "Successfully loaded GPU kernel \(label).")
+ }
+}
+
+
+
diff --git a/Source/Serrano/core/graph.swift b/Source/Serrano/core/graph.swift
new file mode 100644
index 0000000..b70bc98
--- /dev/null
+++ b/Source/Serrano/core/graph.swift
@@ -0,0 +1,114 @@
+//
+// compute_graph.swift
+// serrano
+//
+//
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+
+
+public protocol GraphSupportedBindingDataType {}
+
+extension Array: GraphSupportedBindingDataType {}
+extension Tensor: GraphSupportedBindingDataType {}
+extension Float: GraphSupportedBindingDataType {}
+extension Double: GraphSupportedBindingDataType {}
+extension Int: GraphSupportedBindingDataType {}
+
+/**
+Basic graph protocol
+*/
+public protocol Graph {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// List of `GraphSymbol`.
+ /// Key is the `UID` of symbol object in value field.
+ var symbols: [String: GraphSymbol] {get set}
+
+ /// The readable label of this graph
+ var graphLabel: String {get set}
+
+ /// If this graph is trainable.
+ var trainable: Bool {get set}
+
+ /// Description of this graph
+ var description: String {get}
+
+ /// Optimizer of this graph doing backward training.
+ /// Could be `nil` if just do forward calcualtion.
+ var optimizer: Optimizer? {get set}
+
+ /// Counter of backward training
+ var epoch: Int {get}
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Create symbols
+
+
+ /// Add a `TensorSymbol` to the graph.
+ /// - Parameters:
+ /// - label: label description
+ /// - shape: shape description
+ /// - Returns: return value description
+ func tensor(_ label: String?, shape: TensorShape) -> TensorSymbol
+
+ /// Add a `ScalarSymbol` to the graph.
+ ///
+ /// - Parameter label: label
+ /// - Returns: A `ScalarSymbol`
+ func scalar(_ label: String?, dataType: TensorDataType) -> ScalarSymbol
+
+ /// Add a `OperatorSymbol` to the graph.
+ ///
+ /// - Parameters:
+ /// - inputs: input array of `TensorSymbol`
+ /// - op: A `ComputableOperator` instance
+ /// - Returns: Output `TensorSymbol` from `operator` calculation, and constructed `OperatorSymbol`
+ func operation(_ label: String?, inputs: [TensorSymbol], op: ComputableOperator) -> (outputTensorSymbols: [TensorSymbol], operatorSymbol: OperatorSymbol, paramSymbols: [GraphSymbol])
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - compute
+
+ /// Forward computing from inputs to outputs.
+ ///
+ /// - Parameter mode: computation mode
+ /// - Returns: Array of tensors/scalars. `nil` if met errors.
+ func forward(mode: OperatorComputationMode) -> [DataSymbolSupportedDataType]?
+
+ /// Backward computing the grads for updatable data symbols.
+ ///
+ /// - Parameters:
+ /// - mode: computation mode
+ func backward(mode: OperatorComputationMode)
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - other
+
+ /// Bind data to `TensorSymbol`.
+ ///
+ /// - Parameter data: A dictinary whose key is `label` of a `TensorSymbol`
+ /// and value is an array of `DataSymbolSupportedDataType` objects.
+ func bindData(_ data: [String: DataSymbolSupportedDataType])
+
+ /// Add symbol to `symbols`.
+ /// Should check duplicate
+ ///
+ /// - Parameter symbol: new symbol
+ func addSymbols(_ symbol: GraphSymbol)
+}
+
+extension Graph {
+ /// Add symbol to `symbols`.
+ /// Should check duplicate
+ ///
+ /// - Parameter symbol: new symbol
+ public func addSymbols(_ symbol: GraphSymbol) {
+ if self.symbols[symbol.UID] == nil {
+ var g = self as Graph
+ g.symbols[symbol.UID] = symbol
+ }
+ }
+}
diff --git a/Source/Serrano/core/model.swift b/Source/Serrano/core/model.swift
new file mode 100644
index 0000000..8483db8
--- /dev/null
+++ b/Source/Serrano/core/model.swift
@@ -0,0 +1,68 @@
+//
+// model.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 8/4/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+
+/**
+ModelCallback defines a serires of APIs a callback object for models.
+*/
+public protocol ModelCallback {
+ // TODO: IMPLEMENT
+}
+
+/**
+This protocol defines higher-level APIs for creating, training and prediction of a model.
+*/
+public protocol Model: Graph {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// Operator symbols of this model
+ var operators: [OperatorSymbol] {get}
+
+ /// List of input tensor symbols
+ var inputs: [TensorSymbol] {get}
+
+ /// List of output tensor symbol
+ var outputs: [TensorSymbol] {get}
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Constructing models
+
+ /// Add an input entry for moedel
+ ///
+ /// ## inputShape
+ /// `inputShape` indicates the shape of a sample without batch index.
+ /// For example if we have some 128x128 RGB picture as input, the `inputShape` should be `TensorShape`
+ /// object with `shapeArray`: `[128, 128, 3]` if `channelOrder` is 'TensorChannelOrder.last'
+ ///
+ /// - Parameter inputShape: input shape of sample. Not include batch index.
+ /// - Returns: tensor symbol representation
+ func addInput(inputShape: TensorShape, channelOrder: TensorChannelOrder) -> TensorSymbol
+
+ /// Add layer to the model.
+ ///
+ /// - Parameters:
+ /// - inputs: list of input tensor symbols to this layer
+ /// - op: operator
+ /// - Returns: list of output tensor symbols from this layer
+ func addLayer(_ inputs: [TensorSymbol], op: ComputableOperator) -> [TensorSymbol]
+
+ func configure()
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Training
+
+ // TODO: IMPLEMENT
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Prediction
+
+ // TODO: IMPLEMENT
+
+}
diff --git a/Source/Serrano/core/operator.swift b/Source/Serrano/core/operator.swift
new file mode 100644
index 0000000..6562009
--- /dev/null
+++ b/Source/Serrano/core/operator.swift
@@ -0,0 +1,207 @@
+//
+// Operator.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 3/17/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import Metal
+
+/**
+The delegate support methods tracking the calculation status and result of associated `Operator` object.
+Operator could assign attribute `computationDelegate` to a instance conforms to this protocol.
+The instanced could track the calculation status of operator.
+ */
+public protocol OperatorCalculationDelegate {
+ /**
+ Tell the delegate this `Operator` will begin to calcualte the output tensor
+
+ - Parameters:
+ - op: The calculation operator
+ */
+ func operatorWillBeginComputation(_ op: ComputableOperator)
+
+ /**
+ Tell the delegate this `Operator` has done the calcualtion.
+
+ - Parameters:
+ - op: The calcaulation operator.
+ - tensor: The calcualted output tensors object.
+ */
+ func operatorDidEndComputation(_ op: ComputableOperator, outputTensors tensors: [Tensor])
+
+ /// Tell the delegate this operator will begin grads calculation
+ ///
+ /// - Parameter op: op
+ func operatorWillBeginGradsComputation(_ op: ComputableOperator)
+
+ /// Tell the delegate this operator end grads claculation
+ ///
+ /// - Parameters:
+ /// - op: op
+ /// - tensors: grads tensor
+ func operatorDidEndGradsComputation(_ op: ComputableOperator, grads: [String: DataSymbolSupportedDataType])
+
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+public enum OperatorComputationMode {
+ case CPU
+ case GPU
+ case Auto
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class OperatorUtils {
+
+ /// If target operator's `computationDelegate` is `nil`, logging warning.
+ ///
+ /// - Parameters:
+ /// - op: target operator conforms to `ComputableOperator`
+ /// - file: checking file name
+ /// - function: checking function name
+ /// - line: checking code line number
+ public static func delegateNilWarning(op: T, file: String, function: String, line: Int) {
+ if op.computationDelegate == nil {
+ SerranoLogging.warningLogging(message: "Call async computation without assigning computate delegate. The output will never be fetched.", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+/// Types of operator's input-output mapping.
+public enum OperatorMappingType {
+ /// N-to-N
+ case OneToOne
+
+ /// N-to-1
+ case Constant
+}
+
+
+/**
+ This protocol defines the common computation APIs of `Operator`.
+ */
+public protocol ComputableOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /// MARK: - Attributes
+
+ /// Computation delegate.
+ ///
+ /// The assigned delegate can track the computation status and \
+ /// result through methods from `OperatorComputationDelegate`.
+ ///
+ /// Usually the delegate is a `Flow` object.
+ var computationDelegate: OperatorCalculationDelegate? {get set}
+
+ /// Kernel function name
+ var metalKernelFuncLabel: String {get}
+
+ /// Operator readable label
+ var operatorLabel: String {get set}
+
+ /// Input tensors to operate
+ var inputTensors: [Tensor]? {get set}
+
+ /// Output tensors
+ var outputTensors: [Tensor]? {get set}
+
+ /// If `true`, operator will not call `inputOutputTensorsCheck()` before doing calculation.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ var disableInputOutputCheck: Bool {get set}
+
+ /// Indicate if this operator would do paramter update
+ var trainable: Bool {get set}
+
+ /// The mapping type of this operator
+ var mapType: OperatorMappingType {get}
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /// MARK: - Computation realted methods (forward)
+
+ /**
+ Calulate the output tensor shape given an input tensor shape.
+ If the operator cannot operate on the input tensor shape, return `nil`.
+
+ - Parameters:
+ - shapeArray: An array of `TensorShape`
+
+ - Returns: A `TensorShape` object or `nil` if the operator could not operate on the input shape.
+ */
+ func outputShape(shapeArray shapes:[TensorShape]) -> [TensorShape]?
+
+ /// Check if the input tensors and output tensors's shape matching
+ ///
+ /// - Returns: `check` is `true` if match; `msg` error info if not match
+ func inputOutputTensorsCheck() -> (check: Bool, msg: String)
+
+ /// Compute sync
+ ///
+ /// - Parameter computationMode: computationMode
+ func compute(_ computationMode: OperatorComputationMode)
+
+ /// Compute the output tensor asyncally. Output result will be passed to `computationDelegate`.
+ ///
+ /// - note: If the `computationDelegate` is nil, the computed output will be lost.
+ func computeAsync(_ computationMode: OperatorComputationMode)
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /// MARK: - Differentiaion realted methods (backward)
+
+
+ /// Compute grads from output against each input tensor and involving parameters.
+ ///
+ /// ## Identify corresponding input
+ /// The returned label of data could be used to identify its correspoding input
+ /// following below rules:
+ /// - __Input tensor__. `input_{i}` where `i` is the corresponding input tensor's index in `inputTensors`
+ /// - __Parameter__. The parameter's name.
+ ///
+ /// - Note: Operator will not store grads tensor. If the returned value not used,
+ /// grads will lost.
+ ///
+ /// - Parameter computationMode: computationMode description
+ /// - Returns: grads list for each input tensor and involving parameters with label
+ func gradCompute(_ computationMode: OperatorComputationMode) -> [String: DataSymbolSupportedDataType]
+
+ /// Compute async grads from output against each input tensor and involving parameters.
+ ///
+ /// - Parameter computationMode: computationMode
+ /// - Parameter upGrds: Optional. Grads from upstream operators in a Graph computation.
+ func gradComputAsync(_ computationMode: OperatorComputationMode)
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /// MARK: - Support symbolic graph computation
+
+ /// This function is called when add an operator to a `Graph` with function `operation()`.
+ /// A `Graph` object is returned by this function representing the inner structure of this operator.
+ /// Some complex operators may consist of other simple operators and we want explicitly show the structure
+ /// in the graph it added to.
+ /// So the returned graph will be merged into the graph calling this function.
+ ///
+ /// - Parameter InputSymbols: input symbols for this graph
+ /// - Returns: a graph object
+// func addedToGraph(with InputSymbols: [TensorSymbol]) -> Graph
+
+ /// Bind data from symbol to parameter of this operator.
+ ///
+ /// - Parameters:
+ /// - symbols: binded symbols
+ func bindParamSymbols(_ symbols: [GraphSymbol])
+
+ /// An array of `GraphSymbol` for this operator's parameters.
+ /// This array may be empty if operator needs no parameter.
+ /// This function is used in constructing computaion graph.
+ ///
+ /// - Returns: An array.
+ func paramSymbols() -> [GraphSymbol]
+
+}
+
diff --git a/Source/Serrano/core/optimizer.swift b/Source/Serrano/core/optimizer.swift
new file mode 100644
index 0000000..954d252
--- /dev/null
+++ b/Source/Serrano/core/optimizer.swift
@@ -0,0 +1,69 @@
+//
+// optimizer.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 9/26/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+
+/// Learning rate decay method
+public enum LearningRateDecayMethod {
+ /// Each epoch update following: `lr = lr_initial - decay * t` where `t` is current epoch..
+ case Step
+
+ /// Each epoch udpate following: `lr = lr_initial * e^(-decay * t)` where `t` is current epoch.
+ case Exponential
+
+ /// Each epoch udpate following: `lr = lr_initial /(1 + decay * t)` where `t` is current epoch.
+ case Inverse
+
+
+ /// Calculate decayed lr for current epoch.
+ ///
+ /// - Parameters:
+ /// - initialLR: initial lr
+ /// - decay: decay hyperparameter
+ /// - Returns: decayed lr
+ func decayLR(initialLR: Float, decay: Float, epoch: Int) -> Float {
+ var lr: Float = 0.0
+ if self == LearningRateDecayMethod.Step {
+ lr = initialLR - decay * Float(epoch)
+ } else if self == LearningRateDecayMethod.Exponential {
+ lr = initialLR * exp(-decay*Float(epoch))
+ } else {
+ lr = initialLR / (1 + decay * Float(epoch))
+ }
+ return lr
+ }
+}
+
+
+
+/**
+This protocol defines the API and behavior of an optimizer.
+*/
+public protocol Optimizer {
+ //// Initial set learning reate
+ var initLearningRate: Float {get}
+
+ /// Learning rate of current epoch
+ var learningRate: Float {set get}
+
+ /// Decay method
+ var decayMethod: LearningRateDecayMethod {get set}
+
+ /// Do preapre work before 1st backward if needed.
+ func prepare(_ graph: Graph)
+
+ /// Update a data symbol's updated value
+ ///
+ //// - Parameters:
+ /// - dataSymbol: target symbol
+ /// - gradValue: gradvalue fot this time updating
+ func updateParameter(_ dataSymbol: DataSymbol, gradValue: DataSymbolSupportedDataType)
+}
+
+extension Optimizer {
+}
diff --git a/Source/Serrano/core/resource_manager.swift b/Source/Serrano/core/resource_manager.swift
new file mode 100644
index 0000000..fe1a9ed
--- /dev/null
+++ b/Source/Serrano/core/resource_manager.swift
@@ -0,0 +1,430 @@
+//
+// resource_manager.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/15/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import Metal
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// MARK:
+
+/**
+A `MTLBufferResource` contains the allocated `MTLBuffer` related information for a `Tensor` object managed by a resource manager.
+*/
+public struct MTLBufferResource {
+ /// `MTLBuffer` object
+ var buffer: MTLBuffer
+
+ /// Offset from the buffer base address.
+ /// Used by sliced tensor object.
+ var offset: Int
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// MARK:
+
+public enum SerranoTensorStatus {
+ case Idle
+ case Occupy
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// MARK:
+
+/**
+This is a framework level class
+*/
+public class SerranoResourceManager {
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// Table tracking allocated Tensor and its corresponding MTLBuffer
+ public var tensorBufferTable: [Tensor: MTLBuffer]
+
+ /// The dictionary tracking the usage status of tensor
+ public var tensorStatusTable: [Tensor: SerranoTensorStatus]
+
+ /// The operation queue when operate on `tensorBufferTable` and `tensorStatus`
+ public var operationQueue: DispatchQueue
+
+ /// Readable label
+ public var label: String
+
+ /// Description
+ public var description: String {
+ get {
+ return "SerranoResourceManager(label: \(self.label))"
+ }
+ }
+
+ // Global resource manager
+ public static let globalManager = SerranoResourceManager(label: "gloabal_resource_manager")
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializer
+
+ public init(label: String = "Resource Manager") {
+ self.tensorBufferTable = [Tensor: MTLBuffer]()
+ self.tensorStatusTable = [Tensor: SerranoTensorStatus]()
+ self.operationQueue = DispatchQueue(label: "serrano.resourceQueue")
+ self.label = label
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Allocate unmanaged tensors
+
+
+ /// Allocate unmanaged tensors for shapes.
+ /// `Unmanaged` means that the alloated tensors will not be hold via strong refrence by manager.
+ ///
+ /// - Parameter shapes: shapes description
+ /// - Returns: return value description
+ public func allocateUnamangedTensors(_ shapes: [TensorShape]) -> [Tensor] {
+ var tensors = [Tensor]()
+ for shape in shapes {
+ tensors.append(Tensor(repeatingValue: 0.0, tensorShape: shape))
+ }
+ return tensors
+ }
+
+
+ /// Allocate unmanaged tensors for shapes.
+ /// `Unmanaged` means that the alloated tensors will not be hold via strong refrence by manager.
+ ///
+ /// - Parameter shapes: shapes description
+ /// - Returns: return value description
+ public func allocateUnamangedTensor(_ shape: TensorShape) -> Tensor {
+ let tensor = Tensor(repeatingValue: 0.0, tensorShape: shape)
+ SerranoLogging.stdLogging(message: "Allocate unmanged tensor \(tensor.description)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)",
+ loggingLevel: SerranoLoggingType.LowLevel)
+ return tensor
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Allocate unmanaged MTLBuffers
+
+ /// Allocate unamanged `MTLBuffers`.
+ ///
+ /// - Warning: If engine has no available GPU device, `fatalError` raised.
+ ///
+ /// - Parameter tensors: target tensors
+ /// - Returns: Array of `MTLBuffer`
+ public func allocateUnmanagedMTLBuffers(_ tensors: [Tensor]) -> [MTLBuffer] {
+ // check gpu available
+ guard SerranoEngine.configuredEngine.hasAvailableGPU() else {
+ SerranoLogging.errorLogging(message: "No available GPU device.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+
+ var buffers = [MTLBuffer]()
+ for tensor in tensors {
+ let newBuffer = SerranoEngine.configuredEngine.GPUDevice!.makeBuffer(bytesNoCopy: tensor.contentsAddress,
+ length: tensor.allocatedBytes,
+ options: MTLResourceOptions.storageModeShared)
+ buffers.append(newBuffer)
+ }
+ return buffers
+ }
+
+ /// Allocate unamanged `MTLBuffer`.
+ ///
+ /// - Warning: If engine has no available GPU device, `fatalError` raised.
+ ///
+ /// - Parameter tensor: target tensor
+ /// - Returns: `MTLBuffer`
+ public func allocateUnmanagedMTLBuffe(_ tensor: Tensor) -> MTLBuffer {
+ return self.allocateUnmanagedMTLBuffers([tensor]).first!
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Allocate managed tensors
+
+
+ /// Request managed tensors for target shapes.
+ ///
+ /// - Note: All tensors requested by this method should be __return__ manually by calling
+ /// `returnTensors(_ tensors: [Tensor])` or `returnTensor(_ tensor: Tensor)`
+ ///
+ /// - Warning: This method is desgined for framework internal usage. Usually user should not
+ /// call this method.
+ ///
+ /// - Parameter shapes: target shapes
+ /// - Returns: tensors
+ public func allocateTensors(_ shapes: [TensorShape]) -> [Tensor] {
+ var tensors = [Tensor]()
+ for shape in shapes {
+ let candidate = self.tensorStatusTable.index(where: { (element) -> Bool in
+ return element.key.capacity >= shape.count && element.value == SerranoTensorStatus.Idle
+ })
+
+ if candidate == nil {
+ // didn't find a reuseable tensor, spawn a new tensor
+ let newTensor = Tensor(repeatingValue: 0.0, tensorShape: shape)
+ self.tensorStatusTable[newTensor] = SerranoTensorStatus.Occupy
+ tensors.append(newTensor)
+
+ // initial a new MTLBuffer for this new Tensor in advance if has available GPU
+ if SerranoEngine.configuredEngine.hasAvailableGPU() {
+ let buffer = SerranoEngine.configuredEngine.GPUDevice!.makeBuffer(bytesNoCopy: newTensor.contentsAddress,
+ length: newTensor.allocatedBytes,
+ options: MTLResourceOptions.storageModeShared)
+ self.tensorBufferTable[newTensor] = buffer
+ }
+
+ SerranoLogging.stdLogging(message: "\(self.description) allocated a new tensor [\(newTensor.description)] to target shape \(shape.shapeArray).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)",
+ loggingLevel: .LowLevel)
+ } else {
+ // reuse
+ let tensor = self.tensorStatusTable[candidate!].key
+ self.tensorStatusTable[tensor] = SerranoTensorStatus.Occupy
+ tensor.shape = shape
+ tensors.append(tensor)
+ SerranoLogging.stdLogging(message: "\(self.description) reused existing tensor [\(tensor.description)] to target shape \(shape.shapeArray).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)",
+ loggingLevel: .LowLevel)
+ }
+
+ }
+
+ return tensors
+ }
+
+
+ /// Allocate single tensor object. This function actually call `allocateTensors(forShapes shapes: [TensorShape])`.
+ ///
+ /// - Note: All tensors requested by this method should be __return__ manually by calling
+ /// `returnTensors(_ tensors: [Tensor])` or `returnTensor(_ tensor: Tensor)`
+ ///
+ /// - Warning: This method is desgined for framework internal usage. Usually user should not
+ /// call this method.
+ ///
+ /// - Parameter tensorShape: shape
+ /// - Returns: tensor
+ public func allocateTensor(_ tensorShape: TensorShape) -> Tensor {
+ return self.allocateTensors([tensorShape]).first!
+ }
+
+
+ /// Return managed tensors.
+ ///
+ /// - Note: Tensor not managed by this manager or is a slice tensor will be ignored.
+ ///
+ /// - Parameter tensors: returned tensors
+ public func returnTensors(_ tensors: [Tensor]) {
+ for tensor in tensors {
+ guard self.isManagingTensor(tensor) else {
+ SerranoLogging.warningLogging(message: "Return a tensor NOT allocated by resource manager.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ continue
+ }
+
+ if tensor.isSliceTensor {
+ SerranoLogging.warningLogging(message: "Received a slice tensor. Only root tensor can be returned.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ continue
+ }
+
+ self.operationQueue.sync {
+ self.tensorStatusTable[tensor] = SerranoTensorStatus.Idle
+ }
+
+ SerranoLogging.stdLogging(message: "Return tensor \(tensor.description) to \(self.description)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)",
+ loggingLevel: .LowLevel)
+ }
+
+ }
+
+ /// Return single tensor to resource manager
+ ///
+ /// - Parameter tensor: tensor
+ public func returnTensor(_ tensor: Tensor) {
+ self.returnTensors([tensor])
+ }
+
+ /// Release target tensors.
+ /// Actually clear corresponding entries in `tensorStatusTable` and `tensorBufferTable`.
+ ///
+ /// - Note: Tensor not managed by this manager or is a slice tensor will be ignored.
+ ///
+ /// - Parameter tensors: target tensors
+ public func releaseTensors(_ tensors: [Tensor]) {
+ for tensor in tensors {
+ // not managing, ignore
+ if !self.isManagingTensor(tensor) {
+ SerranoLogging.warningLogging(message: "Trying to release a tensor \(tensor.description) not managed by this resource manager: [\(self.description)]",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ continue
+ }
+
+ // slice tensor, ignore
+ if tensor.isSliceTensor {
+ SerranoLogging.warningLogging(message: "Trying to release a slice tensor \(tensor.description).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ continue
+ }
+
+ // remove entry
+ let _ = self.tensorStatusTable.remove(at: self.tensorStatusTable.index(forKey: tensor)!)
+
+ // also release attached MTLBuffer if has
+ if self.tensorBufferTable[tensor] != nil{
+ self.tensorBufferTable.remove(at: self.tensorBufferTable.index(forKey: tensor)!)
+ SerranoLogging.stdLogging(message: "Remove tensor \(tensor.description) from resource manager: \(self.description)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)",
+ loggingLevel: SerranoLoggingType.LowLevel)
+ }
+ }
+
+ }
+
+ /// Check if a managed tensor is availabel for reuse
+ /// If the passed in tensor is a sliced tensor, we check the status of its root tensor.
+ ///
+ /// - Note: `false` will be returned if `tensor` is not managed by this manager.
+ public func isTensorAvailable(_ tensor: Tensor) -> Bool {
+ guard self.isManagingTensor(tensor) else {
+ SerranoLogging.warningLogging(message: "Trying to check status of tensor [\(tensor.description)], but tensor was not managed by \(self.description)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return false
+ }
+
+ var result: Bool = true
+ if tensor.isSliceTensor {
+ result = self.tensorStatusTable[tensor.sliceRootTensor!]! == SerranoTensorStatus.Idle
+ } else {
+ result = self.tensorStatusTable[tensor]! == SerranoTensorStatus.Idle
+ }
+
+ return result
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Allocate MTLBufferResource
+
+ /// Request `MTLBufferResource` for tensors.
+ ///
+ /// - Note: If the passed in tensors are not mamaged by this resource manager,
+ /// it will just call `allocateUnmanagedMTLBuffers(_ tensors: [Tensor])`.
+ ///
+ /// - Parameter tensors: target tensors
+ /// - Returns: Array of `MTLBufferResource`
+ public func allocateMTLBufferResources(_ tensors: [Tensor]) -> [MTLBufferResource] {
+ // check gpu available
+ guard SerranoEngine.configuredEngine.hasAvailableGPU() else {
+ SerranoLogging.errorLogging(message: "No available GPU device.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+
+ var bufferResources = [MTLBufferResource]()
+ for tensor in tensors {
+ if !self.isManagingTensor(tensor){
+ let buffer = self.allocateUnmanagedMTLBuffe(tensor)
+ bufferResources.append(MTLBufferResource(buffer: buffer, offset: 0))
+ } else {
+ var buffer: MTLBuffer?
+ var offset = 0
+ var targetTensor = tensor
+
+ // slice tensor just uses root tensor's buffer with offset
+ if tensor.isSliceTensor {
+ targetTensor = tensor.sliceRootTensor!
+ offset = tensor.bytesOffsetFromRootTensor()!
+ }
+
+ // get MTLBuffer
+ self.operationQueue.sync {
+ if self.tensorBufferTable[targetTensor] == nil {
+ // new buffer
+ buffer = SerranoEngine.configuredEngine.GPUDevice!.makeBuffer(bytesNoCopy: targetTensor.contentsAddress,
+ length: targetTensor.allocatedBytes,
+ options: MTLResourceOptions.storageModeShared)
+ // add entry
+ self.tensorBufferTable[targetTensor] = buffer
+ } else {
+ buffer = self.tensorBufferTable[targetTensor]!
+ }
+ }
+
+ bufferResources.append(MTLBufferResource(buffer: buffer!, offset: offset))
+ }
+ }
+ return bufferResources
+ }
+
+ /// Request `MTLBufferResource` for a tensor.
+ ///
+ /// - Note: If the passed in tensor is not mamaged by this resource manager,
+ /// it will just call `allocateUnmanagedMTLBuffer(_ tensor: Tensor)`.
+ ///
+ /// - Parameter tensors: target tensor
+ /// - Returns: `MTLBufferResource`
+ public func allocateMTLBufferResource(_ tensor: Tensor) -> MTLBufferResource {
+ return self.allocateMTLBufferResources([tensor]).first!
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Release managed resources
+
+ /// Release all managed resources
+ ///
+ /// - Warning: This function __does not__ guranteen that all managed tensors and buffers would be released in memory,
+ /// since if tensor or buffer objects are still used in other places, the ARC will nor clear it.
+ /// Before calling this function, the caller must be clear that all managed resources are in idle states.
+ public func releaseAllResources() {
+ self.operationQueue.sync {
+ self.tensorBufferTable.removeAll()
+ self.tensorStatusTable.removeAll()
+ }
+ SerranoLogging.stdLogging(message: "Resource manager: \(self.description) release all managed tensors and buffers",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)",
+ loggingLevel: SerranoLoggingType.LowLevel)
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+ /// Check if tensor is managed by this manager.
+ /// If a tensor is managed by this manager, all its sliced tensors are managed by this manager.
+ ///
+ /// - Note: If the tensor is a sliced tensor, we will check if managing its root tensor.
+ ///
+ /// - Parameter tensor: tensor description
+ /// - Returns: return value description
+ public func isManagingTensor(_ tensor: Tensor) -> Bool {
+ var result:Bool = true
+ var checkTensor: Tensor = tensor
+ if tensor.isSliceTensor { checkTensor = tensor.sliceRootTensor! }
+ // check
+ self.operationQueue.sync {
+ result = self.tensorStatusTable[checkTensor] != nil
+ }
+ return result
+ }
+
+ /// Check if a MTLBuffer is managed by this manager
+ ///
+ /// - Parameter buffer: buffer description
+ /// - Returns: return value description
+ public func isManagingBufferr(_ buffer: MTLBuffer) -> Bool {
+ var result: Bool = false
+ self.operationQueue.sync {
+ result = self.tensorBufferTable.index(where: { (element) -> Bool in
+ return element.value.contents() == buffer.contents()
+ }) != nil
+ }
+ return result
+ }
+}
diff --git a/Source/Serrano/core/symbol.swift b/Source/Serrano/core/symbol.swift
new file mode 100644
index 0000000..30d7ece
--- /dev/null
+++ b/Source/Serrano/core/symbol.swift
@@ -0,0 +1,432 @@
+//
+// symbol.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/23/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+
+
+/// Description
+public enum SymbolType {
+ case Tensor
+ case Operator
+ case Scalar
+
+ /// If enum is data symbol
+ ///
+ /// - Returns: return value description
+ func isDataSymbol() -> Bool {
+ return self == SymbolType.Tensor || self == SymbolType.Scalar
+ }
+}
+
+
+
+
+/// The Data source of a data symbol (Tensor or scalar symbol)
+public enum SymbolDataSource {
+ /// Symbol explicitly created by user and needs feeding from user
+ case User
+
+ /// Symbol gets data from a calculation result, so no need sfeeding from user
+ case Calculation
+
+ /// Symbol has a default value but also can receive data from user,
+ /// like some parameters of operators.
+ case Default
+
+ /// other cases
+ case Other
+}
+
+/// Generate 6-length random string from `[a-zA-Z0-9]`
+///
+/// - Returns: ID
+public func serranoSymbolUIDGenerate() -> String {
+ let IDLength = 6
+ let letters: NSString = "9876543210abcdefghijklMNOPQRSTUVWXYZ0123456789mnopqrstuvwxyzABCDEFGHIJKL0123456789"
+ let len = UInt32(letters.length)
+
+ var randomString = ""
+
+ for _ in 0.. Graph
+
+ /// Add new symbol to `inBounds`.
+ /// Should check duplicate.
+ func addToInBound(_ symbol: GraphSymbol)
+
+ /// Add new symbol to `outBounds`
+ /// Should check duplicate.
+ func addToOutBound(_ symbol: GraphSymbol)
+
+ /// Evaluate the symbol to get value.
+ /// Returns maybe `nil`.
+ ///
+ /// - Returns: [DataSymbolSupportedDataType]
+ func evaluate() -> [DataSymbolSupportedDataType]?
+}
+
+extension GraphSymbol {
+
+ /// Generate a `ComputationGraph` object according to `inBounds` and `outBounds` information of this symbol.
+ /// The generated graph ending with this symbol.
+ ///
+ /// - Returns: A `ComputationGraph` object.
+ public func generateOutputGraph(_ label: String? = nil) -> Graph {
+ var graphLabel = label
+ if graphLabel == nil { graphLabel = "ComputationGraph(Generate from symbol \(self.symbolLabel)[\(self.UID)])" }
+
+ var symbolQueue = [GraphSymbol]()
+ symbolQueue.append(self)
+
+ let graph = ComputationGraph(graphLabel!)
+ while !symbolQueue.isEmpty {
+ // pop first
+ let symbol = symbolQueue.first!
+ symbolQueue.remove(at: 0)
+
+ graph.addSymbols(symbol)
+ symbolQueue.append(contentsOf: symbol.inBounds)
+ }
+
+ return graph
+ }
+}
+
+public protocol DataSymbol: GraphSymbol {
+ /// Data source
+ var dataSource: SymbolDataSource {get set}
+
+ /// Indicate if grads of this data symbol should be used to update itself.
+ ///
+ /// ## updatable of DataSymbol V.S. enabledParameterUpdate of OperatorSymbol
+ /// Both attribute controls data updating in backward training.
+ /// `updatable` of a data symbol controls whether updating this symbol's binded data,
+ /// while `enabledParameterUpdate` controls whether updating operator symbol's inbounds data symbols.
+ /// If `enabledParameterUpdate` is `false`, Serrano will ignore `updatable` attributes of
+ /// all data symbols in this operator symbol's inbounds.
+ /// So user can think `enabledParameterUpdate` has high priority over `updatable` on controlling.
+ var updatable: Bool {get set}
+
+ /// This attribute hold the grad of this data symbol in this epoch training.
+ /// This grad value is chained value (i.e. grad of final output of the grpah against this data)
+ ///
+ /// - Note: `currentGrad` will be added to `historyGrads` at the end of each backward training.
+ var currentGrad: DataSymbolSupportedDataType? {get set}
+
+ /// Binded data
+ var bindedData: DataSymbolSupportedDataType? {get set}
+
+ /// Bind data to data symbol
+ ///
+ /// - Parameter data: data to bind
+ /// - Returns: if binded successfully
+ func bindData(_ data:DataSymbolSupportedDataType) -> Bool
+
+ /// This function used to update a data symbol's value and grad during backward.
+ ///
+ /// - Parameters:
+ /// - optimizer: optimizer
+ /// - gradValue: this epoch calculated value
+ /// - upGrads: up grads from backward chain
+ mutating func backwardUpdate(_ optimizer: Optimizer, gradValue: DataSymbolSupportedDataType, upGrads:[DataSymbolSupportedDataType])
+}
+
+extension DataSymbol {
+
+ /// This function used to update a data symbol's value during backward.
+ ///
+ /// 1. Calculate chained grads
+ /// 2. Use optimizer updaing bined value
+ /// 3. Store this epoch's grad
+ ///
+ /// - Parameters:
+ /// - optimizer: optimizer
+ /// - gradValue: this epoch calculated value
+ /// - upGrads: up grads from backward chain
+ public mutating func backwardUpdate(_ optimizer: Optimizer, gradValue: DataSymbolSupportedDataType, upGrads:[DataSymbolSupportedDataType]) {
+ // chained grads
+ if self.symbolType == SymbolType.Scalar {
+ // Oprator takes in tensors and outputs tensors
+ // and I cannot think of any operator where a scalar parameter is trainable.
+ // So for scalar symbol, I doubt if we needs to calcualte its grads....
+ // FIXME: If you think it is not neccessary.
+ let gradValueFloat = (gradValue as! SupportedScalarDataType).floatValue
+ var gradValue:Float = 0.0
+ for upGrad in upGrads {
+ if upGrad is Tensor {
+ // tensor up grad, do reduce sum
+ let upGradTensor = upGrad as! Tensor
+ let gradTensor = Tensor(repeatingValue: 0.0, tensorShape: TensorShape(dataType: .float, shape: [1]))
+ let reduceOp = ReduceSumOperator(inputTensors: [upGradTensor], outputTensors: [gradTensor],
+ axis:Array(0.. [DataSymbolSupportedDataType]?`
+extension ScalarSymbol {
+ public func evaluate() -> [DataSymbolSupportedDataType]? {
+ if bindedData == nil {
+ return nil
+ } else {
+ return [self.bindedData as! DataSymbolSupportedDataType]
+ }
+ }
+}
+
+/**
+Symbol represents a tensor object
+*/
+public protocol TensorSymbol: DataSymbol {
+ /// Shpe of this tensor symbol
+ var shape: TensorShape {get set}
+}
+
+/// Default implementation of `evaluate() -> Tensor`
+extension TensorSymbol {
+ public func evaluate(_ mode: OperatorComputationMode = .GPU) -> [DataSymbolSupportedDataType]? {
+ let graph = self.generateOutputGraph()
+ let result = graph.forward(mode: mode)
+ if result == nil {
+ return nil
+ } else {
+ return result! as [DataSymbolSupportedDataType]
+ }
+ }
+}
+
+/**
+Symbol represent an operator calcualtion
+*/
+public protocol OperatorSymbol: GraphSymbol {
+ /// Input symbols for this operator
+ ///
+ /// - Note: symbols in this attribute should also be included in `inBounds`
+ var inputSymbols: [TensorSymbol] {get set}
+
+ /// Input shapes tensor symbols in `inputSymbols`
+ var inputTensorShapes: [TensorShape] {get set}
+
+ /// Parameters (weights, bias etc.) symbols for this operator.
+ /// For operator having no params, this should be an empty array.
+ ///
+ /// - Note: symbols in this attribute should also be included in `inBounds`
+ var paramSymbols: [DataSymbol] {get set}
+
+ /// The operator instance
+ var serranoOperator: ComputableOperator {get set}
+
+ /// Control if update asscoiated operator's parameter
+ ///
+ /// ## updatable of DataSymbol V.S. enabledParameterUpdate of OperatorSymbol
+ /// Both attribute controls data updating in backward training.
+ /// `updatable` of a data symbol controls whether updating this symbol's binded data,
+ /// while `enabledParameterUpdate` controls whether updating operator symbol's inbounds data symbols.
+ /// If `enabledParameterUpdate` is `false`, Serrano will ignore `updatable` attributes of
+ /// all data symbols in this operator symbol's inbounds.
+ /// So user can think `enabledParameterUpdate` has high priority over `updatable` on controlling.
+ var enabledParameterUpdate: Bool {get set}
+
+ /// Get output symbols of this operator
+ ///
+ /// - Returns: Array of SerranoTensorSymbol.
+ func outputSymbols() -> [TensorSymbol]
+
+ /// Add to `paramSymbols`
+ ///
+ /// - Parameter symbol: new symbol
+ func addToParamSymbols(_ symbol: GraphSymbol)
+
+ /// Get grads on any inbounds symbol
+ ///
+ /// - Parameter inputSymbol:
+ /// - Returns: SupportedGradsDataType
+ func gradsOnInput(_ inputSymbol: DataSymbol) -> DataSymbolSupportedDataType
+
+ /// Get corresponding inbound symbol for a label following rule in `Operator` method
+ /// `gradCompute(_:)`.
+ ///
+ /// - Parameter label: label
+ /// - Returns: DataSymbol
+ func inboundSymbolForGradLabel(_ label: String) -> DataSymbol?
+
+ /// For a given `symbol` in inbounds, return a list of output symbols that
+ /// it involves with calculation.
+ ///
+ /// - Parameter symbol: target symbol
+ /// - Returns: list of output symbol
+ func gradRelatedOutputSymbols(onInboundSymbol symbol: DataSymbol) -> [DataSymbol]
+}
+
+/// Default implementation of `evaluate() -> [Tensor]`
+extension OperatorSymbol {
+ public func evaluate(_ mode: OperatorComputationMode = .GPU) -> [DataSymbolSupportedDataType]? {
+ let graph = self.generateOutputGraph()
+ return graph.forward(mode: mode)
+ }
+
+ /// Add to `paramSymbols`
+ ///
+ /// - Parameter symbol: new symbol
+ public func addToParamSymbols(_ symbol: GraphSymbol) {
+ if !self.paramSymbols.contains { (graphSymbol) -> Bool in
+ return graphSymbol.UID == symbol.UID
+ } {
+ var o = self as OperatorSymbol
+ guard symbol is DataSymbol else {
+ SerranoLogging.errorLogging(message: "Symbol argument is not a DataSymbol type.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("Fatalerror raised by Serrano. Check log for details")
+ }
+ o.paramSymbols.append(symbol as! DataSymbol)
+ }
+ }
+
+ /// Get grads on any inbounds symbol.
+ /// Call attached operator's `gradCompute(_:, :)` to get grads.
+ /// Use return tensor's label to identify corresponding input datasymbol.
+ ///
+ /// ## Identify corresponding input
+ /// The tensor label of returned tensors could be used to identify its correspoding input
+ /// following below rules:
+ /// - __Input tensor__. `input_{i}` where `i` is the corresponding input tensor's index in `inputTensors`
+ /// - __Parameter__. The parameter's name.
+ ///
+ /// - Note: If `inputSymbol` does not belong to this operator symbol.
+ /// `fatalError()` raised.
+ ///
+ /// - Parameter inputSymbol:
+ /// - Returns: SupportedGradsDataType
+ public func gradsOnInput(_ inputSymbol: DataSymbol) -> DataSymbolSupportedDataType {
+ // check belonging
+ guard (self.inBounds.contains {$0.UID == inputSymbol.UID}) else {
+ SerranoLogging.errorLogging(message: "Symbol argument is not a DataSymbol type.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("Fatalerror raised by Serrano. Check log for details")
+ }
+
+ let label:String
+ if (self.inputSymbols.contains {inputSymbol.UID == $0.UID}) {
+ label = "input_\(self.inputSymbols.index {$0.UID == inputSymbol.UID}!)"
+ } else {
+ // We just use symbol's label her cause param symbol is generated by operator's paramSymbols() function.
+ // This function will assgin proper labels.
+ label = inputSymbol.symbolLabel
+ }
+
+ let grad = self.serranoOperator.gradCompute(.GPU).filter { $0.key == label}.first?.value
+ guard grad != nil else {
+ SerranoLogging.errorLogging(message: "Unexpected error. Could not find grads for target input.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("Fatalerror raised by Serrano. Check log for details")
+ }
+ return grad!
+ }
+
+ /// Get inbound data symbol for target label.
+ ///
+ /// ## Identify corresponding input
+ /// The returned label of data could be used to identify its correspoding input
+ /// following below rules:
+ /// - __Input tensor__. `input_{i}` where `i` is the corresponding input tensor's index in `inputTensors`
+ /// - __Parameter__. The parameter's name.
+ ///
+ /// - Note: may return `nil`
+ ///
+ /// - Parameter label: label
+ /// - Returns: symbol
+ public func inboundSymbolForGradLabel(_ label: String) -> DataSymbol? {
+ let symbol: DataSymbol?
+ if label.contains("input") {
+ // input tensor symbol
+ symbol = self.inputSymbols[Int(label.split(separator: "_").last!)!]
+ } else {
+ // parameter data symbol
+ symbol = self.paramSymbols.filter {$0.symbolLabel == label}.first
+ }
+ return symbol
+ }
+
+ /// For a given `symbol` in inbounds, return a list of output symbols that
+ /// it involves with calculation.
+ ///
+ /// - Parameter symbol: target symbol
+ /// - Returns: list of output symbol
+ public func gradRelatedOutputSymbols(onInboundSymbol symbol: DataSymbol) -> [DataSymbol] {
+ var symbols = [DataSymbol]()
+ if self.serranoOperator.mapType == OperatorMappingType.Constant {
+ symbols.append(self.outBounds[0] as! DataSymbol)
+ } else {
+ let index = self.inputSymbols.index(where: {$0.UID == symbol.UID})
+ if index != nil {
+ // input symbol
+ symbols.append(self.outBounds[index!] as! DataSymbol)
+ } else {
+ // param symbols, all output
+ symbols.append(contentsOf: self.outBounds[index!] as! [DataSymbol])
+ }
+ }
+ return symbols
+ }
+}
+
diff --git a/Source/Serrano/core/tensor.swift b/Source/Serrano/core/tensor.swift
new file mode 100644
index 0000000..7f989b4
--- /dev/null
+++ b/Source/Serrano/core/tensor.swift
@@ -0,0 +1,1770 @@
+//
+// tensor.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 3/15/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import Accelerate
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// MARK:
+
+/**
+ Defined the supported data type stored in Tensor object.
+
+ Should be matching with supported scalar data types in
+ [Apple's Metal specification](https://developer.apple.com/metal/metal-shading-language-specification.pdf) (section 2.1)
+
+ Currently compatible with v1.2 with types:
+
+ - int32: A signed two’s complement 32-bit integer
+ - uint32: An unsigned 32-bit integer
+ - float16: A 16-bit floating-point
+ - float32: A 32-bit floating-point
+ */
+public enum TensorDataType {
+ ///A signed two’s complement 32-bit integer
+ case int
+ case float
+ case double
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// MARK:
+
+public class TensorUtils {
+ /**
+ Randomlly generate a `Tensor` object.
+ */
+ public static func generateRandomTensor(targetTensorShape shape: TensorShape, maxVal: Float) -> Tensor{
+ var array = [SupportedScalarDataType]()
+ switch shape.dataType {
+ case .double:
+ for _ in 0.. [Float] {
+ var flatten = [Float]()
+ for element in array {
+ if element is SupportedNestedType {
+ flatten.append(contentsOf: TensorUtils.flattenArray(array: element as! [Any], dataType: type))
+ } else if element is SupportedScalarDataType {
+ switch type {
+ case .int:
+ flatten.append(Float(element as! Int))
+ case .float:
+ flatten.append(element as! Float)
+ case .double:
+ flatten.append(Float(exactly: element as! Double)!)
+ }
+ } else {
+ SerranoLogging.warningLogging(message: "Meet unsupported datatype: \(type(of: element))", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ }
+ }
+
+ return flatten
+ }
+}
+
+
+infix operator .==: ComparisonPrecedence
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// MARK:
+
+/**
+ The tensor shape description. Specify the shape and data type of a `Tensor` data object.
+
+ ## dataType
+
+
+ ## Shape
+ The `shape` attrribute defines the dimension of a `Tensor` object. In `serrano`, we follow `row-marjor`
+ order to store and access elements in a `Tensor` object and each __row__ is represented as an array.
+ For a given shape array with `n` indices `[i_0, i_1, ..., i_(n-1)]`, each index from `i_0` to `i_(n-2)` defines the number of rows in its
+ previous dimension. The last index define the number of elements in its previous dimention.
+ For example, a `TensorShape` object with `shpae` as `[2, 1, 3]`.
+ It's 1st dimension has `2` rows in which each row has `1` row with 3 elements.
+
+
+ User should be clear and unserstanding what a `Tensor` object _looks_ like when they passing in a `TensorShape` argument.
+ For example, a `Tensor` object with shape `[2, 3]`, it can be visulized as a nested array like below:
+ ```
+ // shape [2, 3]
+ [
+ [1.0, 0.5, 1.3],
+ [2.0, 4.2, 6.7],
+ ]
+ ```
+ And a typical real-world example, a 3-channel RGB image data could be represented with shape `[3, image_hight, image_width]`:
+ ```
+ [
+ // R channel frame
+ [
+ [232, ..., 123], // (# of Int elements) = image_width
+ .
+ .
+ .
+ [113, ..., 225]
+ ], // (# of Array elements) = image_hight
+
+ // G channel frame
+ [
+ [232, ..., 123],
+ .
+ .
+ .
+ [113, ..., 225]
+ ],
+
+ // B channel frame
+ [
+ [232, ..., 123],
+ .
+ .
+ .
+ [113, ..., 225]
+ ]
+ ]
+ ```
+
+## Equatable
+Two TensorShape objects are __equal__ (`==`)if they have the same `shape`.
+
+Two TensorShape objects are __dot equal__ (`.==`) if they have the same `shape` and same `dataType`.
+
+## Rank is `0`
+If a `Tensor` object's shapeArray has `0` rank, it indicates that it just contains a __scalar__ value.
+ */
+public struct TensorShape: Equatable, Comparable {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Properties
+
+ /// Data type
+ var dataType:TensorDataType
+
+ /// Shape array
+ var shapeArray: [Int]
+
+ /// Rank of dimensions
+ var rank: Int {
+ get {
+ return self.shapeArray.count
+ }
+ }
+
+ /// Element count
+ var count: Int {
+ get {
+ return self.shapeArray.reduce(1, *)
+ }
+ }
+
+ /// Description
+ var description: String {
+ get {
+ return "TensorShape(rank: \(self.rank), shape: \(self.shapeArray), dataType:\(self.dataType))"
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializer
+
+
+ /// Public init for out init
+ ///
+ /// - Parameters:
+ /// - dataType: dataType
+ /// - shape: shape
+ public init(dataType: TensorDataType, shape: [Int]) {
+ self.dataType = dataType
+ self.shapeArray = shape
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+ /// Check if `shape` array is valid
+ func shapeVerify() -> Bool {
+ if self.shapeArray.count == 0 {
+ return false
+ }
+
+ for dimensionSize in self.shapeArray {
+ if dimensionSize <= 0 {
+ return false
+ }
+ }
+ return true
+ }
+
+ /// Get `shape` array in reversed
+ func reversedShapArray() -> [Int] {
+ return Array(self.shapeArray.reversed())
+ }
+
+ /// Get transposed shape of current shape.
+ /// - Note: only works for shapes with rank values as `2`. Otherwise, `fatalError` will be throws.
+ func transposed() -> TensorShape {
+ guard self.rank == 2 else {
+ SerranoLogging.errorLogging(message: "Trying to get transposed shape from shape with rank \(self.rank)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ return TensorShape(dataType: self.dataType, shape: [self.shapeArray[1], self.shapeArray[0]])
+ }
+
+
+ /// Tow shapes dot equals when have same `dataType` and `==`.
+ ///
+ /// - Parameters:
+ /// - shapeA: shape A
+ /// - shapeB: shape B
+ public static func .==(shapeA: TensorShape, shapeB: TensorShape) -> Bool {
+ return shapeA.dataType == shapeB.dataType && shapeA == shapeB
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Equable protocol
+
+ /// Two shap equals when has same rank and same dimensions.
+ ///
+ /// - Parameters:
+ /// - shapeA: shapeA description
+ /// - shapeB: shapeB description
+ /// - Returns: return value description
+ public static func ==(shapeA: TensorShape, shapeB: TensorShape) -> Bool {
+ return shapeA.rank == shapeB.rank && shapeA.shapeArray.elementsEqual(shapeB.shapeArray)
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Comparable protocol
+
+
+ /// `shapeA` larger than `shapeB` if:
+ /// - `shapeA` has a larger `rank` or
+ /// - `shapeA` and `shapeB` has same rank while `shapeA` has a larger element `count`
+ ///
+ /// - Parameters:
+ /// - shapeA: shapeA description
+ /// - shapeB: shapeB description
+ /// - Returns: return value description
+ public static func >(shapeA: TensorShape, shapeB: TensorShape) -> Bool {
+ if shapeA.rank > shapeB.rank {
+ return true
+ } else if shapeA.rank == shapeB.rank {
+ return shapeA.count > shapeB.count
+ } else {
+ return false
+ }
+ }
+
+ /// `shapeA` larger than or equal to `shapeB` if:
+ /// - `shapeA` has larger `rank` or
+ /// - `shapeA` and `shapeB` has same rank while `shapeA` has a larger or same`count`
+ ///
+ /// - Parameters:
+ /// - shapeA: shapeA description
+ /// - shapeB: shapeB description
+ /// - Returns: return value description
+ public static func >=(shapeA: TensorShape, shapeB: TensorShape) -> Bool {
+ if shapeA.rank > shapeB.rank {
+ return true
+ } else if shapeA.rank == shapeB.rank {
+ return shapeA.count >= shapeB.count
+ } else {
+ return false
+ }
+ }
+
+ /// `shapeA` less than `shapeB` if:
+ /// - `shapeA` has smaller `rank` or
+ /// - `shapeA` and `shapeB` has same rank while `shapeA` has a smaller `count`
+ ///
+ /// - Parameters:
+ /// - shapeA: shapeA description
+ /// - shapeB: shapeB description
+ /// - Returns: return value description
+ public static func <(shapeA: TensorShape, shapeB: TensorShape) -> Bool {
+ if shapeA.rank < shapeB.rank {
+ return true
+ } else if shapeA.rank == shapeB.rank {
+ return shapeA.count < shapeB.count
+ } else {
+ return false
+ }
+ }
+
+ /// `shapeA` less than or equal to `shapeB` if:
+ /// - `shapeA` has smaller `rank` or
+ /// - `shapeA` and `shapeB` has same rank while `shapeA` has a smaller or same `count`
+ ///
+ /// - Parameters:
+ /// - shapeA: shapeA description
+ /// - shapeB: shapeB description
+ /// - Returns: return value description
+ public static func <=(shapeA: TensorShape, shapeB: TensorShape) -> Bool {
+ if shapeA.rank < shapeB.rank {
+ return true
+ } else if shapeA.rank == shapeB.rank {
+ return shapeA.count <= shapeB.count
+ } else {
+ return false
+ }
+ }
+
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// MARK: Operators
+
+/// Custom Operator: in-place addition of two tensors or tensor-scalar, result stored in left tensor.
+infix operator &+: AdditionPrecedence
+
+/// Custom Operator: in-place substraction of two tensors or tensor-scalar, result stored in left tensor.
+infix operator &-: AdditionPrecedence
+
+/// Custom Operator: in-place multiplication of two tensors or tensor-scalar, result stored in left tensor.
+infix operator &*: MultiplicationPrecedence
+
+/// Custom Operator: in-place division of two tensors or tensor-scalar, result stored in left tensor.
+infix operator &/: MultiplicationPrecedence
+
+
+
+/// Custom Operator: broadcast addition between two tensors.
+infix operator .+: AdditionPrecedence
+
+/// Custom Operator: broadcast substraction between two tensors.
+infix operator .-: AdditionPrecedence
+
+/// Custom Operator: broadcast multiplication between two tensors.
+infix operator .*: MultiplicationPrecedence
+
+/// Custom Operator: broadcast division between two tensors.
+infix operator ./: MultiplicationPrecedence
+
+
+
+/// Custom Operator: in-place broadcast addition of two tensors or tensor-scalar, result stored in left tensor.
+infix operator .&+: AdditionPrecedence
+
+/// Custom Operator: in-place broadcast substraction of two tensors or tensor-scalar, result stored in left tensor.
+infix operator .&-: AdditionPrecedence
+
+/// Custom Operator: in-place broadcast multiplication of two tensors or tensor-scalar, result stored in left tensor.
+infix operator .&*: MultiplicationPrecedence
+
+/// Custom Operator: in-place broadcast division of two tensors or tensor-scalar, result stored in left tensor.
+infix operator .&/: MultiplicationPrecedence
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//MARK:
+
+/**
+A `Tensor` object is a n-dimension page-aligned data container with fixed-size memory space.
+The attribute `shape` specifying the dimension information of a `Tensor` object.
+
+## All elements stored as `Float` values
+No matter what type of array user passing in: `Double` or `Float` or `Int`, inside a `Tensor` object
+values will be converted and stored as `Float` (32-bit signle precision).
+`Serrano` chose this because 32-bit `Float` is the maximum precise floating data type `Metal` could support (v1.2).
+
+
+## Inside Memory Layout
+Inside a `Tensor` object, it maintains a _virtual_ 1-d array as a contiguous memory space manually allocated.
+Elements are stored following `row-major` order. Details can be found in `TensorShape` docs.
+
+
+## Tensor-Tensor arithmetic operators
+Besides [Operators]() which contains many math and NN operations, `Tensor` object itself implements/overloads common
+arithmetic operators.
+Tensor object support element-wise arithmetic operation with or without broadcasting.
+Also it supports in-place operation choice.
+
+### Element-wise operation without broadcasting:
+- `+` Addition without broadcasting
+- `-` Substraction without broadcasting
+- `*` Multiplication without broadcasting
+- `/` Division without broadcasting
+
+### Element-wise in-place operation:
+- `&+` Addition without broadcasting
+- `&-` Substraction without broadcasting
+- `&*` Multiplication without broadcasting
+- `&/` Division without broadcasting
+
+### Element-wise operation with broadcasting:
+- `.+` Addition without broadcasting
+- `.-` Substraction without broadcasting
+- `.*` Multiplication without broadcasting
+- `./` Division without broadcasting
+
+### Element-wise in-place operation with broadcasting:
+- `.&+` Addition without broadcasting
+- `.&-` Substraction without broadcasting
+- `.&*` Multiplication without broadcasting
+- `.&/` Division without broadcasting
+
+Example usage:
+```swift
+/// Element wise addition without broadcasting
+let tensorA = Tensor(repeatingValue: 1.0,
+ tensorShape: TensorShape(dataType:.float, shape: [2, 5]))
+let tensorB = Tensor(repeatingValue: 2.0,
+ tensorShape: TensorShape(dataType:.float, shape: [2, 5]))
+let result1 = tensorA + tensorB
+
+/// Element wise in-place addition without broadcasting
+let result2 = tensorA &+ tensorB
+print(result2 == tensorA) // true
+
+/// Element wise addition with broadcasting
+let tensorD = Tensor(repeatingValue: 2.0,
+ tensorShape: TensorShape(dataType:.float, shape: [1, 5]))
+let result3 = tensorA .+ tensorD
+
+/// Element wise in-place addition with broadcasting
+let resunt4 = tensorA .&+ tensorD
+print(result4 == tensorA) // true
+```
+
+## Directly used as TensorSymbol
+`Tensor` conforms to `TensorSymbol` protocol.
+So a tensor object could be used as a symbol participating in graph computation.
+
+ */
+public class Tensor: Hashable, Equatable, TensorSymbol {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Properties
+
+ /// shape
+ internal var _shape: TensorShape
+
+ /// Allocated aligned memory size in bytes
+ internal var _allocatedSize: Int
+
+ /// The base address of allocated memory.
+ internal var _dataMemoryBaseAdrress: UnsafeMutablePointer
+
+ /// Convenience `Float` reader
+ internal var _elementReader: UnsafeMutableBufferPointer
+
+ /// How many elements can store
+ internal var _capacity: Int
+
+ /// Slice marker.
+ /// Indicate if this tensor object is a slice from another tensor.
+ /// If `true`, this object is just a reference slice and should not own the memeory.
+ internal var _sliceMarker: Bool = false
+
+ /// The root tensor of a sliced tensor
+ internal var _sliceRootTensor: Tensor? = nil
+
+ /// The parent tensor of a sliced tensor
+ internal var _sliceParentTensor: Tensor? = nil
+
+ /// The index of this slice object in raw tensor.
+ /// `nil` if not a sliced tensor
+ internal var _sliceIndex: Int?
+
+ /// Count of data elements tored
+ public var count: Int {
+ get {
+ return self._shape.shapeArray.reduce(1, *)
+ }
+ }
+
+
+ /// Dimension of the array
+ public var dimension: Int {
+ get {
+ return self._shape.shapeArray.count
+ }
+ }
+
+ /// How many elements can store
+ public var capacity: Int {
+ get {
+ return self._capacity
+ }
+ }
+
+
+ /// Shape
+ public var shape: TensorShape {
+ get {
+ return self._shape
+ }
+ set(newShape) {
+ self._shape = newShape
+ }
+ }
+
+ /// Allocated memeory bytes
+ public var allocatedBytes: Int {
+ get {
+ return self._allocatedSize
+ }
+ }
+
+ /// Readable description of a object
+ public var description: String {
+ get {
+ return "TensorObject at \(self._dataMemoryBaseAdrress), Allocated bytes: \(self._allocatedSize), Element count: \(self._shape.shapeArray.reduce(1, *)), shape: \(self._shape.shapeArray)"
+ }
+ }
+
+ /// A readable label for distinguishing different tensors. Serrano does not check label unique.
+ public var label: String = "Tensor"
+
+ /// Base address of allocated memeory space
+ ///
+ /// - Warning:
+ /// User could reuse a `Tensor` object with this pointer by assigning new values.
+ /// However, it must be very careful with the boundary.
+ public var contentsAddress: UnsafeMutablePointer {
+ get {
+ return self._dataMemoryBaseAdrress
+ }
+ }
+
+ /// Float value reader pointer
+ public var floatValueReader: UnsafeMutableBufferPointer {
+ get {
+ return self._elementReader
+ }
+ }
+
+ /// Rank
+ public var rank: Int {
+ get {
+ return self._shape.shapeArray.count
+ }
+ }
+
+ /// Slice marker.
+ /// Indicate if this tensor object is a slice from another tensor.
+ /// If `true`, this object is just a reference slice and should not own the memeory.
+ public var isSliceTensor: Bool {
+ get {
+ return self._sliceMarker
+ }
+ }
+
+ /// The root tensor of a sliced tensor
+ /// - Note: `nil` if `_sliceMarker` is `false`
+ public var sliceRootTensor: Tensor? {
+ get {
+ return self._sliceRootTensor
+ }
+ }
+
+
+ /// The parent tensor of a sliced tensor
+ /// - Note: `nil` if `_sliceMarker` is `false`
+ public var sliceParentTensor: Tensor? {
+ get {
+ return self._sliceParentTensor
+ }
+ }
+
+ /// The index of this slice object in raw tensor.
+ /// `nil` if not a sliced tensor
+ public var sliceIndex: Int? {
+ get {
+ return self._sliceIndex
+ }
+ }
+
+ /// Conforms to protocol `TensorSymbol`.
+ public var symbolType: SymbolType = SymbolType.Tensor
+
+ /// Conforms to protocol `TensorSymbol`.
+ public var UID: String = ""
+
+ /// Conforms to protocol `TensorSymbol`.
+ public var symbolLabel: String = ""
+
+ /// Inbound symbols list. Conforms to `GraphSymbol`.
+ /// To prevent from cycle reference, we dynamic constructing this attribute from `inBoundsWeak`.
+ public var inBounds: [GraphSymbol] {
+ get {
+ return self.inBoundsWeak.filter {$0.value != nil}.map {$0.value!}
+ }
+ set(bounds) {
+ for symbol in bounds {
+ self.addToInBound(symbol)
+ }
+ }
+ }
+
+ /// Outbound symbols list. Conforms to `GraphSymbol`.
+ /// To prevent from cycle reference, we dynamic constructing this attribute from `inBoundsWeak`.
+ public var outBounds: [GraphSymbol] {
+ get {
+ return self.outBoundsWeak.filter {$0.value != nil}.map {$0.value!}
+ }
+ set(bounds) {
+ for symbol in bounds {
+ self.addToOutBound(symbol)
+ }
+ }
+ }
+
+ /// Weak reference array of inbounds objects
+ internal var inBoundsWeak: [WeakSerranoGraphSymbol] = [WeakSerranoGraphSymbol]()
+
+ /// Weak reference array of outbounds objects
+ internal var outBoundsWeak: [WeakSerranoGraphSymbol] = [WeakSerranoGraphSymbol]()
+
+ /// __Read-only__. Conforms to protocol `TensorSymbol`.
+ ///
+ /// - Note: If a tensor object used as a `TensorSymbol`, atrribute `bindedData` could not be modifed.
+ public var bindedData: DataSymbolSupportedDataType? {
+ get {
+ return self
+ }
+ set {
+ SerranoLogging.errorLogging(message: "Could not modify the bindedData of a TensorSymbol represented from a tensor object.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ }
+
+ /// Conforms to protocol `TensorSymbol`.
+ public var dataSource: SymbolDataSource = SymbolDataSource.Other
+
+ /// If differentiable
+ public var updatable = false
+
+ /// Current grad
+ public var currentGrad: DataSymbolSupportedDataType?
+
+ /// If enabled history grads recording.
+ /// Default is `false`.
+ public var historyGradsEnabled = false
+
+ /// history grads
+ public var historyGrads: [DataSymbolSupportedDataType] = [Tensor]()
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - subscript
+
+
+ /// Check if index is valid for fetching a single element
+ ///
+ /// - Parameter index: index description
+ /// - Returns: return value description
+ public func indexIsValid(_ index: [Int]) -> Bool {
+ if index.count != self._shape.shapeArray.count {
+ return false
+ } else {
+ for dimension in 0..= self._shape.shapeArray[dimension] || index[dimension] < 0 {
+ return false
+ }
+ }
+ return true
+ }
+ }
+
+
+
+ /// Get offset in terms of element counting from valid index
+ ///
+ /// - Parameter index: valid index list
+ /// - Returns: offset
+ internal func offSetFromIndex(_ index: [Int]) -> Int {
+ var offset = 0
+ for i in 0..<(index.count-1) {
+ offset += index[i] * self._shape.shapeArray.suffix(from: i+1).reduce(1, *)
+ }
+ offset += index.last!
+ return offset
+ }
+
+ /// Custom subscript to fetch single `Float` element
+ ///
+ /// - Parameter index: Indices list
+ subscript(_ index: Int...) -> Float {
+ set(newValue) {
+ guard indexIsValid(index) else {
+ fatalError("[serrano]Invalid index \(index) for tensor with shape \(self._shape.shapeArray)")
+ }
+ let offset = self.offSetFromIndex(index)
+ self._elementReader[offset] = newValue
+ }
+
+ get {
+ guard indexIsValid(index) else {
+ fatalError("[serrano]Invalid index \(index) for tensor with shape \(self._shape.shapeArray)")
+ }
+ let offset = self.offSetFromIndex(index)
+ return (self._dataMemoryBaseAdrress + offset).pointee
+ }
+ }
+
+ /// Custom subscript to fetch single `Float` element
+ ///
+ /// - Parameter index: Indices list
+ subscript(_ index: [Int]) -> Float {
+ set(newValue) {
+ guard indexIsValid(index) else {
+ fatalError("[serrano]Invalid index \(index) for tensor with shape \(self._shape.shapeArray)")
+ }
+ let offset = self.offSetFromIndex(index)
+ self._elementReader[offset] = newValue
+ }
+
+ get {
+ guard indexIsValid(index) else {
+ fatalError("[serrano]Invalid index \(index) for tensor with shape \(self._shape.shapeArray)")
+ }
+ let offset = self.offSetFromIndex(index)
+ return (self._dataMemoryBaseAdrress + offset).pointee
+ }
+ }
+
+
+ /// Internal use, already checked boundary before fetch or set
+ ///
+ /// - Warning: May cause unexpected result or fatal error if `index` is not valid.
+ ///
+ /// - Parameter index: index array
+ public subscript(withoutChecking index:[Int]) -> Float {
+ set(newValue) {
+ let offset = self.offSetFromIndex(index)
+ self._elementReader[offset] = newValue
+ }
+
+ get {
+ let offset = self.offSetFromIndex(index)
+ return (self._dataMemoryBaseAdrress + offset).pointee
+ }
+ }
+
+
+ /// Get element value from `index`.
+ /// If input index is invalid, return `missingValue`.
+ ///
+ /// - Parameters:
+ /// - index: index array
+ /// - missingValue: default value for missing elements. Default is `0.0`
+ /// - Returns: value
+ public func fetchValueOrDefault(_ index: [Int], missingValue: Float = 0.0) -> Float {
+ if indexIsValid(index) {
+ return self[withoutChecking:index]
+ } else {
+ return missingValue
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+
+ /// Designated init.
+ ///
+ /// - Note: Usually, user should not call this init directly.
+ ///
+ /// - Parameters:
+ /// - shape: shape
+ /// - allocateSize: allocateSize
+ /// - capacity: capacity
+ /// - dataMemoryBaseAddres: dataMemoryBaseAddres
+ /// - elementReader: elementReader
+ public init(shape: TensorShape, allocateSize: Int, capacity: Int,
+ dataMemoryBaseAddres: UnsafeMutablePointer, elementReader:UnsafeMutableBufferPointer) {
+ self._shape = shape
+ self._allocatedSize = allocateSize
+ self._capacity = capacity
+ self._dataMemoryBaseAdrress = dataMemoryBaseAddres
+ self._elementReader = elementReader
+ }
+
+ /// Initial a tensor with repeating value
+ ///
+ /// - Parameters:
+ /// - repeatingValue: repeat value
+ /// - shape: shape
+ public convenience init(repeatingValue value: Float, tensorShape shape: TensorShape) {
+ let pageSize: Int = Int(getpagesize())
+ let needSize = shape.count * MemoryLayout.stride
+ let allocateSize = needSize.padded(alignmentSize: pageSize)!
+ let capacity = allocateSize / MemoryLayout.stride
+ var memory: UnsafeMutableRawPointer? = nil
+ posix_memalign(&memory, pageSize, allocateSize) // use posix_memalign to make it work on MAC
+ let dataMemoryBaseAddress = UnsafeMutablePointer(OpaquePointer(memory!))
+ let elementReader = UnsafeMutableBufferPointer(start: dataMemoryBaseAddress, count: capacity)
+
+ self.init(shape: shape,
+ allocateSize: allocateSize,
+ capacity: capacity,
+ dataMemoryBaseAddres: dataMemoryBaseAddress,
+ elementReader: elementReader)
+
+ // initial
+ self._dataMemoryBaseAdrress.initialize(to: value, count: capacity)
+ }
+
+ /// Construct a tensor from a flatten (1-D) swift array.
+ ///
+ /// - Parameters:
+ /// - array: 1-D array.
+ /// - shape: The shape which the constructed tensor have. The count of this shape should be the same of count of passed in array.
+ public convenience init(fromFlatArray array:[SupportedScalarDataType], tensorShape shape: TensorShape) {
+ self.init(repeatingValue: 0.0, tensorShape: shape)
+
+ // Initialize values
+ // Note: initialize(from:count:) not used here cause we can not make sure passed in array is contiguous.
+ for i in 0..,
+ count: Int, shape: TensorShape, index: Int) {
+ self._dataMemoryBaseAdrress = sliceContentAddress
+ self._elementReader = UnsafeMutableBufferPointer(start: self._dataMemoryBaseAdrress, count: count)
+ self._shape = shape
+ self._capacity = count
+ self._sliceMarker = true
+ self._sliceParentTensor = parentTensor
+ self._sliceIndex = index
+ self._allocatedSize = count * MemoryLayout.stride
+ // setup root
+ if parentTensor.isSliceTensor {
+ // parent is a sliced tensor
+ self._sliceRootTensor = parentTensor.sliceRootTensor!
+ } else {
+ // parent is a normal tensor
+ self._sliceRootTensor = parentTensor
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Deinitializers
+
+ /// Need to free allcoated memory space manually.
+ deinit {
+ if !self._sliceMarker { // Slice tensor could not free memeory
+ free(self._dataMemoryBaseAdrress)
+
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Util methods
+
+ /// Reload data from a flat array.
+ ///
+ /// - Note: If `array` size < tensor's `count`
+ ///
+ /// - Parameter array:
+ public func reloadData(fromFlatArray array:[SupportedScalarDataType], tensorShape shape: TensorShape) {
+ guard self.capacity >= shape.count else {
+ SerranoLogging.errorLogging(message: "Trying to load a data array larer than tensor's capacity size. Capacity \(self.capacity), shape count \(shape.count), array count: \(array.count)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+
+ for i in 0.. [Float] {
+ var faltArray = [Float]()
+ for offset in 0.. [Any] {
+ if self.rank == 0 {
+ return [self.floatValueReader[0]]
+ } else {
+ return constructNestedArrayFrom(location: [])
+ }
+ }
+
+
+ /// Recursively construct nested array
+ ///
+ /// - Parameter shape: shape needs to construct
+ /// - Returns: result
+ internal func constructNestedArrayFrom(location: [Int]) -> [Any] {
+ let currDimSize = self._shape.shapeArray[location.count]
+ var array = [Any]()
+
+ if location.count == self._shape.shapeArray.count - 1 {
+ var offset = 0
+ for i in 0.. Bool {
+ return lhs._dataMemoryBaseAdrress == rhs._dataMemoryBaseAdrress
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Tensor manipulation
+
+ /// Return a new tensor contains the absolute values of this tensor.
+ /// Same effect as using `AbsOperator`.
+ ///
+ /// - Returns: new tensor object with absed values.
+ public func abs() -> Tensor {
+ let newTensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(self.shape)
+ let absOp = AbsOperator(inputTensors: [self], outputTensors: [newTensor])
+ absOp.compute()
+ return newTensor
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Tensor-Tensor arithmetic operations (not support broadcasting)
+
+
+ /// Element-wise addition. __Not support braodcasting__.
+ /// Same effect as using `AddOperator`.
+ ///
+ /// - Warning: If two tensors don't have same shape, program would be aborted (`fatalError()` called).
+ ///
+ /// - Parameters:
+ /// - left: left tensor
+ /// - right: right tensor
+ /// - Returns: result tensor
+ public static func +(left: Tensor, right:Tensor) -> Tensor {
+ let outputTensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(left.shape)
+ let op = AddOperator(inputTensors: [left, right], outputTensors: [outputTensor])
+ op.compute()
+ return outputTensor
+ }
+
+ /// Element-wise substraction. __Not support braodcasting__.
+ /// Same effect as using `SubOperator`.
+ ///
+ /// - Warning: If two tensors don't have same shape, program would be aborted (`fatalError()` called).
+ ///
+ /// - Parameters:
+ /// - left: left tensor
+ /// - right: right tensor
+ /// - Returns: result tensor
+ public static func -(left: Tensor, right:Tensor) -> Tensor {
+ let outputTensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(left.shape)
+ let op = SubOperator(inputTensors: [left, right], outputTensors: [outputTensor])
+ op.compute()
+ return outputTensor
+ }
+
+ /// Element-wise multiplication. __Not support braodcasting__.
+ /// Same effect as using `AddOperator`.
+ ///
+ /// - Warning: If two tensors don't have same shape, program would be aborted (`fatalError()` called).
+ ///
+ /// - Parameters:
+ /// - left: left tensor
+ /// - right: right tensor
+ /// - Returns: result tensor
+ public static func *(left: Tensor, right:Tensor) -> Tensor {
+ let outputTensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(left.shape)
+ let op = MultOperator(inputTensors: [left, right], outputTensors: [outputTensor])
+ op.compute()
+ return outputTensor
+ }
+
+ /// Element-wise division. __Not support braodcasting__.
+ /// Same effect as using `DivOperator`.
+ ///
+ /// - Warning: If two tensors don't have same shape, program would be aborted (`fatalError()` called).
+ ///
+ /// - Parameters:
+ /// - left: left tensor
+ /// - right: right tensor
+ /// - Returns: result tensor
+ public static func /(left: Tensor, right:Tensor) -> Tensor {
+ let outputTensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(left.shape)
+ let op = DivOperator(inputTensors: [left, right], outputTensors: [outputTensor])
+ op.compute()
+ return outputTensor
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Tensor-Tensor in-place arithmetic operations (not support broadcasting)
+
+ /// Element-wise in-place addition. __Not support braodcasting__.
+ /// Same effect as using `AddOperator`.
+ ///
+ /// - Note: This is an __in place__ operation.
+ /// Result stored in left tensor and return the calcualted left tensor.
+ ///
+ /// - Warning: If two tensors don't have same shape, program would be aborted (`fatalError()` called).
+ ///
+ /// - Parameters:
+ /// - left: left tensor
+ /// - right: right tensor
+ /// - Returns: left tensor after calculated.
+ @discardableResult public static func &+(left: Tensor, right:Tensor) -> Tensor {
+ let op = AddOperator(inputTensors: [left, right], outputTensors: [left])
+ op.compute()
+ return left
+ }
+
+ /// Element-wise in-place substraction. __Not support braodcasting__.
+ /// Same effect as using `SubOperator`.
+ ///
+ /// - Note: This is an __in place__ operation.
+ /// Result stored in left tensor and return the calcualted left tensor.
+ ///
+ /// - Warning: If two tensors don't have same shape, program would be aborted (`fatalError()` called).
+ ///
+ /// - Parameters:
+ /// - left: left tensor
+ /// - right: right tensor
+ /// - Returns: left tensor after calculated.
+ @discardableResult public static func &-(left: Tensor, right:Tensor) -> Tensor {
+ let op = SubOperator(inputTensors: [left, right], outputTensors: [left])
+ op.compute()
+ return left
+ }
+
+ /// Element-wise in-place multiplication. __Not support braodcasting__.
+ /// Same effect as using `MultOperator`.
+ ///
+ /// - Note: This is an __in place__ operation.
+ /// Result stored in left tensor and return the calcualted left tensor.
+ ///
+ /// - Warning: If two tensors don't have same shape, program would be aborted (`fatalError()` called).
+ ///
+ /// - Parameters:
+ /// - left: left tensor
+ /// - right: right tensor
+ /// - Returns: left tensor after calculated.
+ @discardableResult public static func &*(left: Tensor, right:Tensor) -> Tensor {
+ let op = MultOperator(inputTensors: [left, right], outputTensors: [left])
+ op.compute()
+ return left
+ }
+
+ /// Element-wise in-place division. __Not support braodcasting__.
+ /// Same effect as using `DivOperator`.
+ ///
+ /// - Note: This is an __in place__ operation.
+ /// Result stored in left tensor and return the calcualted left tensor.
+ ///
+ /// - Warning: If two tensors don't have same shape, program would be aborted (`fatalError()` called).
+ ///
+ /// - Parameters:
+ /// - left: left tensor
+ /// - right: right tensor
+ /// - Returns: left tensor after calculated.
+ @discardableResult public static func &/(left: Tensor, right:Tensor) -> Tensor {
+ let op = DivOperator(inputTensors: [left, right], outputTensors: [left])
+ op.compute()
+ return left
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Tensor-Tensor arithmetic operations (support broadcasting)
+
+ /// Element-wise addition. __support braodcasting__.
+ /// Same effect as using `BrodcastAddOperator`.
+ ///
+ /// - Warning: If two tensors don't have same shape or one of them cannot be broadcasted to anoter one,
+ /// program would be aborted (`fatalError()` called).
+ ///
+ /// - Parameters:
+ /// - left: left tensor
+ /// - right: right tensor
+ /// - Returns: result tensor
+ public static func .+(left: Tensor, right:Tensor) -> Tensor {
+ let outputTensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(left.shape)
+ let op = BroadcastAddOperator(inputTensors: [left, right], outputTensors: [outputTensor])
+ op.compute()
+ return outputTensor
+ }
+
+ /// Element-wise substraction. __support braodcasting__.
+ /// Same effect as using `BroadcastSubOperator`.
+ ///
+ /// - Warning: If two tensors don't have same shape or one of them cannot be broadcasted to anoter one,
+ /// program would be aborted (`fatalError()` called).
+ ///
+ /// - Parameters:
+ /// - left: left tensor
+ /// - right: right tensor
+ /// - Returns: result tensor
+ public static func .-(left: Tensor, right:Tensor) -> Tensor {
+ let outputTensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(left.shape)
+ let op = BroadcastSubOperator(inputTensors: [left, right], outputTensors: [outputTensor])
+ op.compute()
+ return outputTensor
+ }
+
+ /// Element-wise multiplication. __support braodcasting__.
+ /// Same effect as using `BroadcastMultOperator`.
+ ///
+ /// - Warning: If two tensors don't have same shape or one of them cannot be broadcasted to anoter one,
+ /// program would be aborted (`fatalError()` called).
+ ///
+ /// - Parameters:
+ /// - left: left tensor
+ /// - right: right tensor
+ /// - Returns: result tensor
+ public static func .*(left: Tensor, right:Tensor) -> Tensor {
+ let outputTensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(left.shape)
+ let op = BroadcastMultOperator(inputTensors: [left, right], outputTensors: [outputTensor])
+ op.compute()
+ return outputTensor
+ }
+
+ /// Element-wise division. __support braodcasting__.
+ /// Same effect as using `BroadcastDivOperator`.
+ ///
+ /// - Warning: If two tensors don't have same shape or one of them cannot be broadcasted to anoter one,
+ /// program would be aborted (`fatalError()` called).
+ ///
+ /// - Parameters:
+ /// - left: left tensor
+ /// - right: right tensor
+ /// - Returns: result tensor, a new created tensor.
+ public static func ./(left: Tensor, right:Tensor) -> Tensor {
+ let outputTensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(left.shape)
+ let op = BroadcastDivOperator(inputTensors: [left, right], outputTensors: [outputTensor])
+ op.compute()
+ return outputTensor
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Tensor-Tensor in-place arithmetic operations (support broadcasting)
+
+ /// Element-wise in-place addition. __support braodcasting__.
+ /// Same effect as using `BrodcastAddOperator`.
+ ///
+ /// - Note: This is an __in place__ operation.
+ /// Result stored in left tensor and return the calcualted left tensor.
+ ///
+ /// - Warning: If two tensors don't have same shape or one of them cannot be broadcasted to anoter one,
+ /// program would be aborted (`fatalError()` called).
+ ///
+ /// - Parameters:
+ /// - left: left tensor
+ /// - right: right tensor
+ /// - Returns: left tensor after calcualted
+ @discardableResult public static func .&+(left: Tensor, right:Tensor) -> Tensor {
+ let op = BroadcastAddOperator(inputTensors: [left, right], outputTensors: [left])
+ op.compute()
+ return left
+ }
+
+ /// Element-wise in-place substraction. __support braodcasting__.
+ /// Same effect as using `BroadcastSubOperator`.
+ ///
+ /// - Note: This is an __in place__ operation.
+ /// Result stored in left tensor and return the calcualted left tensor.
+ ///
+ /// - Warning: If two tensors don't have same shape or one of them cannot be broadcasted to anoter one,
+ /// program would be aborted (`fatalError()` called).
+ ///
+ /// - Parameters:
+ /// - left: left tensor
+ /// - right: right tensor
+ /// - Returns: left tensor after calcualted
+ @discardableResult public static func .&-(left: Tensor, right:Tensor) -> Tensor {
+ let op = BroadcastSubOperator(inputTensors: [left, right], outputTensors: [left])
+ op.compute()
+ return left
+ }
+
+ /// Element-wise in-place multiplication. __support braodcasting__.
+ /// Same effect as using `BroadcastMultOperator`.
+ ///
+ /// - Note: This is an __in place__ operation.
+ /// Result stored in left tensor and return the calcualted left tensor.
+ ///
+ /// - Warning: If two tensors don't have same shape or one of them cannot be broadcasted to anoter one,
+ /// program would be aborted (`fatalError()` called).
+ ///
+ /// - Parameters:
+ /// - left: left tensor
+ /// - right: right tensor
+ /// - Returns: left tensor after calcualted
+ @discardableResult public static func .&*(left: Tensor, right:Tensor) -> Tensor {
+ let op = BroadcastMultOperator(inputTensors: [left, right], outputTensors: [left])
+ op.compute()
+ return left
+ }
+
+ /// Element-wise in-place division. __support braodcasting__.
+ /// Same effect as using `BroadcastDivOperator`.
+ ///
+ /// - Note: This is an __in place__ operation.
+ /// Result stored in left tensor and return the calcualted left tensor.
+ ///
+ /// - Warning: If two tensors don't have same shape or one of them cannot be broadcasted to anoter one,
+ /// program would be aborted (`fatalError()` called).
+ ///
+ /// - Parameters:
+ /// - left: left tensor
+ /// - right: right tensor
+ /// - Returns: left tensor after calcualted
+ @discardableResult public static func .&/(left: Tensor, right:Tensor) -> Tensor {
+ let op = BroadcastDivOperator(inputTensors: [left, right], outputTensors: [left])
+ op.compute()
+ return left
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Tensor-Scalar arithmetic operations.
+
+ /// A tensor plus a scalar variable. Ex. `[2, 3] + 0.5 --> [2.5, 3.5]`.
+ ///
+ /// - Parameters:
+ /// - left: A tensor object
+ /// - rightScalar: A scalar variable
+ /// - Returns: Result tensor, new created.
+ public static func + (left: Tensor, rightScalar: SupportedScalarDataType) -> Tensor {
+ let tensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(left.shape)
+ let outAddress = tensor.contentsAddress
+ let inAddress = left.contentsAddress
+ var scalar = rightScalar.floatValue
+ let count = vDSP_Length(left.count)
+ vDSP_vsadd(inAddress, 1, &scalar, outAddress, 1, count)
+ return tensor
+ }
+
+ /// A tensor substract a scalar variable. Ex. `[2, 3] - 0.5 --> [1.5, 2.5]`.
+ ///
+ /// - Parameters:
+ /// - left: A tensor object
+ /// - rightScalar: A scalar variable
+ /// - Returns: Result tensor, new created.
+ public static func - (left: Tensor, rightScalar: SupportedScalarDataType) -> Tensor {
+ let tensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(left.shape)
+ let outAddress = tensor.contentsAddress
+ let inAddress = left.contentsAddress
+ var scalar = -rightScalar.floatValue
+ let count = vDSP_Length(left.count)
+ vDSP_vsadd(inAddress, 1, &scalar, outAddress, 1, count)
+ return tensor
+ }
+
+ /// A tensor multiply a scalar variable. Ex. `[2, 3] * 0.5 --> [1.0, 1.5]`.
+ ///
+ /// - Parameters:
+ /// - left: A tensor object
+ /// - rightScalar: A scalar variable
+ /// - Returns: Result tensor, new created.
+ public static func * (left: Tensor, rightScalar: SupportedScalarDataType) -> Tensor {
+ let tensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(left.shape)
+ let outAddress = tensor.contentsAddress
+ let inAddress = left.contentsAddress
+ var scalar:Float = rightScalar.floatValue
+ let count = vDSP_Length(left.count)
+ vDSP_vsmul(inAddress, 1, &scalar, outAddress, 1, count)
+ return tensor
+ }
+
+ /// A tensor divide a scalar variable. Ex. `[2, 3] / 0.5 --> [4.0, 6.0]`.
+ ///
+ /// - Parameters:
+ /// - left: A tensor object
+ /// - rightScalar: A scalar variable
+ /// - Returns: Result tensor, new created.
+ public static func / (left: Tensor, rightScalar: SupportedScalarDataType) -> Tensor {
+ let tensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(left.shape)
+ let outAddress = tensor.contentsAddress
+ let inAddress = left.contentsAddress
+ var scalar:Float = rightScalar.floatValue
+ let count = vDSP_Length(left.count)
+ vDSP_vsdiv(inAddress, 1, &scalar, outAddress, 1, count)
+ return tensor
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Tensor-Scalar in-place arithmetic operations.
+
+ /// A tensor plus a scalar variable. Ex. `[2, 3] + 0.5 --> [2.5, 3.5]`.
+ ///
+ /// - Note: This is an __in place__ operation.
+ /// Result stored in left tensor and return the calcualted left tensor.
+ ///
+ /// - Parameters:
+ /// - left: A tensor object
+ /// - rightScalar: A scalar variable
+ /// - Returns:
+ @discardableResult public static func &+ (left: Tensor, rightScalar: SupportedScalarDataType) -> Tensor {
+ let outAddress = left.contentsAddress
+ let inAddress = left.contentsAddress
+ var scalar = rightScalar.floatValue
+ let count = vDSP_Length(left.count)
+ vDSP_vsadd(inAddress, 1, &scalar, outAddress, 1, count)
+ return left
+ }
+
+ /// A tensor substract a scalar variable. Ex. `[2, 3] - 0.5 --> [1.5, 2.5]`.
+ ///
+ /// - Note: This is an __in place__ operation.
+ /// Result stored in left tensor and return the calcualted left tensor.
+ ///
+ /// - Parameters:
+ /// - left: A tensor object
+ /// - rightScalar: A scalar variable
+ /// - Returns:
+ @discardableResult public static func &- (left: Tensor, rightScalar: SupportedScalarDataType) -> Tensor {
+ let outAddress = left.contentsAddress
+ let inAddress = left.contentsAddress
+ var scalar = -rightScalar.floatValue
+ let count = vDSP_Length(left.count)
+ vDSP_vsadd(inAddress, 1, &scalar, outAddress, 1, count)
+ return left
+ }
+
+ /// A tensor multiply a scalar variable. Ex. `[2, 3] * 0.5 --> [1.0, 1.5]`.
+ ///
+ /// - Note: This is an __in place__ operation.
+ /// Result stored in left tensor and return the calcualted left tensor.
+ ///
+ /// - Parameters:
+ /// - left: A tensor object
+ /// - rightScalar: A scalar variable
+ /// - Returns: left tensor after calculated.
+ @discardableResult public static func &* (left: Tensor, rightScalar: SupportedScalarDataType) -> Tensor {
+ let outAddress = left.contentsAddress
+ let inAddress = left.contentsAddress
+ var scalar:Float = rightScalar.floatValue
+ let count = vDSP_Length(left.count)
+ vDSP_vsmul(inAddress, 1, &scalar, outAddress, 1, count)
+ return left
+ }
+
+ /// A tensor divide a scalar variable. Ex. `[2, 3] / 0.5 --> [4.0, 6.0]`.
+ ///
+ /// - Note: This is an __in place__ operation.
+ /// Result stored in left tensor and return the calcualted left tensor.
+ ///
+ /// - Parameters:
+ /// - left: A tensor object
+ /// - rightScalar: A scalar variable
+ /// - Returns:
+ @discardableResult public static func &/ (left: Tensor, rightScalar: SupportedScalarDataType) -> Tensor {
+ let address = left.contentsAddress
+ let count = vDSP_Length(left.count)
+ var scalar: Float = rightScalar.floatValue
+ vDSP_vsdiv(address, 1, &scalar, address, 1, count)
+ return left
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Scalar-Tensor arithmetic operations.
+
+ /// A scalar plus a tensor variable. Ex. ` 0.5 + [2, 3] --> [2.5, 3.5]`.
+ ///
+ /// - Parameters:
+ /// - leftScalar: A scalar variable
+ /// - right: A tensor object
+ /// - Returns: Result tensor, new created.
+ public static func + (leftScalar: SupportedScalarDataType, right: Tensor) -> Tensor {
+ let tensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(right.shape)
+ let outAddress = tensor.contentsAddress
+ let inAddress = right.contentsAddress
+ var scalar = leftScalar.floatValue
+ let count = vDSP_Length(right.count)
+ vDSP_vsadd(inAddress, 1, &scalar, outAddress, 1, count)
+ return tensor
+ }
+
+
+ /// A scalar substracts a tensor variable. Ex. ` 0.5 - [2, 3] --> [-1.5, -2.5]`.
+ ///
+ /// - Parameters:
+ /// - leftScalar: A scalar variable
+ /// - right: A tensor object
+ /// - Returns: Result tensor, new created.
+ public static func - (leftScalar: SupportedScalarDataType, right: Tensor) -> Tensor {
+ let tensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(right.shape)
+ let outAddress = tensor.contentsAddress
+ let inAddress = right.contentsAddress
+ var scalar = leftScalar.floatValue
+ var scale:Float = -1.0
+ let count = vDSP_Length(right.count)
+ vDSP_vsmsa(inAddress, 1, &scale, &scalar, outAddress, 1, count)
+ return tensor
+ }
+
+ /// A scalar multiplies a tensor variable. Ex. ` 0.5 * [2, 3] --> [1.0, 1.5]`.
+ ///
+ /// - Parameters:
+ /// - leftScalar: A scalar variable
+ /// - right: A tensor object
+ /// - Returns: Result tensor, new created.
+ public static func * (leftScalar: SupportedScalarDataType, right: Tensor) -> Tensor {
+ let tensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(right.shape)
+ let outAddress = tensor.contentsAddress
+ let inAddress = right.contentsAddress
+ var scalar:Float = leftScalar.floatValue
+ let count = vDSP_Length(right.count)
+ vDSP_vsmul(inAddress, 1, &scalar, outAddress, 1, count)
+ return tensor
+ }
+
+ /// A scalar divides by a tensor variable. Ex. ` 0.5 / [2, 3] --> [0.25, 0.16]`.
+ ///
+ /// - Parameters:
+ /// - leftScalar: A scalar variable
+ /// - right: A tensor object
+ /// - Returns: Result tensor, new created.
+ public static func / (leftScalar: SupportedScalarDataType, right: Tensor) -> Tensor {
+ let tensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(right.shape)
+ let outAddress = tensor.contentsAddress
+ let inAddress = right.contentsAddress
+ var scalar = leftScalar.floatValue
+ let count = vDSP_Length(right.count)
+ vDSP_svdiv(&scalar, inAddress, 1, outAddress, 1, count)
+ return tensor
+ }
+
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Scalar-Tensor in-place arithmetic operations.
+
+ /// A scalar plus a tensor variable. Ex. ` 0.5 + [2, 3] --> [2.5, 3.5]`.
+ ///
+ /// - Note: This is an __in place__ operation.
+ /// Result stored in right tensor and return the calcualted left tensor.
+ ///
+ /// - Parameters:
+ /// - leftScalar: A scalar variable
+ /// - right: A tensor object
+ /// - Returns: Result right tensor.
+ @discardableResult public static func &+ (leftScalar: SupportedScalarDataType, right: Tensor) -> Tensor {
+ let outAddress = right.contentsAddress
+ let inAddress = right.contentsAddress
+ var scalar = leftScalar.floatValue
+ let count = vDSP_Length(right.count)
+ vDSP_vsadd(inAddress, 1, &scalar, outAddress, 1, count)
+ return right
+ }
+
+ /// A scalar substracts a tensor variable. Ex. ` 0.5 - [2, 3] --> [-1.5, -2.5]`.
+ ///
+ /// - Note: This is an __in place__ operation.
+ /// Result stored in right tensor and return the calcualted left tensor.
+ ///
+ /// - Parameters:
+ /// - leftScalar: A scalar variable
+ /// - right: A tensor object
+ /// - Returns: Result right tensor.
+ @discardableResult public static func &- (leftScalar: SupportedScalarDataType, right: Tensor) -> Tensor {
+ let outAddress = right.contentsAddress
+ let inAddress = right.contentsAddress
+ var scalar = leftScalar.floatValue
+ var scale:Float = -1.0
+ let count = vDSP_Length(right.count)
+ vDSP_vsmsa(inAddress, 1, &scale, &scalar, outAddress, 1, count)
+ return right
+ }
+
+ /// A scalar multiplies a tensor variable. Ex. ` 0.5 * [2, 3] --> [1.0, 1.5]`.
+ ///
+ /// - Note: This is an __in place__ operation.
+ /// Result stored in right tensor and return the calcualted left tensor.
+ ///
+ /// - Parameters:
+ /// - leftScalar: A scalar variable
+ /// - right: A tensor object
+ /// - Returns: Result tensor, new created.
+ @discardableResult public static func &* (leftScalar: SupportedScalarDataType, right: Tensor) -> Tensor {
+ let outAddress = right.contentsAddress
+ let inAddress = right.contentsAddress
+ var scalar:Float = leftScalar.floatValue
+ let count = vDSP_Length(right.count)
+ vDSP_vsmul(inAddress, 1, &scalar, outAddress, 1, count)
+ return right
+ }
+
+ /// A scalar divides by a tensor variable. Ex. ` 0.5 / [2, 3] --> [0.25, 0.16]`.
+ ///
+ /// - Note: This is an __in place__ operation.
+ /// Result stored in right tensor and return the calcualted left tensor.
+ ///
+ /// - Parameters:
+ /// - leftScalar: A scalar variable
+ /// - right: A tensor object
+ /// - Returns: Result tensor, new created.
+ @discardableResult public static func &/ (leftScalar: SupportedScalarDataType, right: Tensor) -> Tensor {
+ let outAddress = right.contentsAddress
+ let inAddress = right.contentsAddress
+ var scalar = leftScalar.floatValue
+ let count = vDSP_Length(right.count)
+ vDSP_svdiv(&scalar, inAddress, 1, outAddress, 1, count)
+ return right
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Slice tensor management
+
+
+ /// This function generate a `Tensor` object with attribute `sliceMarker` setting to `true`.
+ /// For cases where batch data samples stored in a single tensor and user wants to access one of the samples,
+ /// this function should be used.
+ ///
+ /// ## `batchIndex`
+ /// The argument `batchIndex` let the function knows which sample to fetch
+ /// and Serrano always assume the first dimension is the batch size.
+ /// For example:
+ /// ```
+ /// /// Suppose we have a tensor with dimensions [2, 3, 4]
+ /// let tensor = ...
+ /// /// This get the 1st batch of the raw tensor and the slice dimensions is [3, 4]
+ /// let slice = tensor.batchSlice(0)
+ /// ```
+ ///
+ /// ## 'nil' result
+ /// The function may return `nil` result in two cases:
+ /// - The `batchIndex` is larger than the `batch size - 1`
+ /// - The tensor object itself does not have at least 2 dimensions
+ ///
+ /// - Parameter batchIndex: the batch index
+ /// - Returns: `Tensor` object
+ public func batchSlice(_ batchIndex: Int) -> Tensor? {
+ guard self.shape.shapeArray.count >= 2 else {
+ SerranoLogging.errorLogging(message: "Tensor \(self.description) trying to generate a batchSlice at index \(batchIndex), but has only \(self.shape.shapeArray.count) dimensions",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ guard batchIndex < self.shape.shapeArray[0] else {
+ SerranoLogging.errorLogging(message: "Tensor \(self.description) trying to generate a batchSlice at index \(batchIndex), but the batch size is \(self.shape.shapeArray[0])",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ let count = self._shape.shapeArray.suffix(from: 1).reduce(1, *)
+ let address = self._dataMemoryBaseAdrress + count * batchIndex
+ let sliceShape = TensorShape(dataType: self._shape.dataType, shape: Array( self._shape.shapeArray.suffix(from: 1)))
+ let sliceTensor = Tensor(sliceTensorFrom: self, sliceContentAddress: address,
+ count: count, shape: sliceShape, index: batchIndex)
+ return sliceTensor
+ }
+
+ /// Check if a slice tensor object belong to this tensor.
+ ///
+ /// - Note: If the passed in tensor is not a slice tensor, always returns `false`.
+ ///
+ /// - Parameter slice: slice tensor
+ /// - Returns: Bool. Result
+ public func containsSlice(_ slice: Tensor) -> Bool {
+ guard slice.isSliceTensor == true else {
+ SerranoLogging.warningLogging(message: "Trying to check a non-silce tensor object.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return false
+ }
+ return slice.sliceRootTensor! == self
+ }
+
+ /// Get the bytes offset for a slice tensor spawned from this tensor.
+ ///
+ /// - Note: The function will first check if contains this slice, if not it will return `nil`
+ ///
+ /// - Parameter slice: slice object
+ /// - Returns: offset in bytes
+ public func slicedTensorOffset(_ slice: Tensor) -> Int? {
+ guard self.containsSlice(slice) else {
+ return nil
+ }
+ return slice.count * slice.sliceIndex!
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Slice tensor methods
+
+ /// The bytes offset from a slice tensor's root tensor.
+ ///
+ /// - Note: Return `nil` if this tensor is not a slice tensor.
+ ///
+ /// - Returns: the bytes offset
+ public func bytesOffsetFromRootTensor() -> Int? {
+ guard self.isSliceTensor else {
+ return nil
+ }
+ var offset = self.sliceParentTensor!.slicedTensorOffset(self)!
+ var tensor = self.sliceParentTensor!
+ while tensor != self.sliceRootTensor {
+ offset += tensor.sliceParentTensor!.slicedTensorOffset(tensor)!
+ tensor = tensor.sliceParentTensor!
+ }
+ return offset
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Protocol TensorSymbol
+
+ /// Return self
+ ///
+ /// - Returns: tensor
+ public func evaluate() -> [DataSymbolSupportedDataType]? {
+ return [self]
+ }
+
+ /// Add new symbol to `inBounds`.
+ /// Should check duplicate.
+ public func addToInBound(_ symbol: GraphSymbol) {
+ let s = symbol as! SerranoGraphSymbol
+ if !(self.inBoundsWeak.contains {$0.value == s}) {
+ let weakSymbol = WeakSerranoGraphSymbol(value: s)
+ self.inBoundsWeak.append(weakSymbol)
+ }
+ }
+
+ /// Add new symbol to `outBounds`
+ /// Should check duplicate.
+ public func addToOutBound(_ symbol: GraphSymbol) {
+ let s = symbol as! SerranoGraphSymbol
+ if !(self.outBoundsWeak.contains {$0.value == s}) {
+ let weakSymbol = WeakSerranoGraphSymbol(value: s)
+ self.outBoundsWeak.append(weakSymbol)
+ }
+ }
+
+ /// A tensor object could not be binded to another tensor.
+ /// - Warning: This function will do nonthing and always return `false`.
+ /// A error logging will be gaven.
+ ///
+ /// - Parameter data: data. Should be a `Tensor` object.
+ /// - Returns: always `False`
+ public func bindData(_ data:DataSymbolSupportedDataType) -> Bool {
+ SerranoLogging.errorLogging(message: "Tensor symbol \(self.symbolLabel) is a tensor object conforms to TensorSymbol. " +
+ "It cannot bind to another tensor object.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return false
+ }
+}
+
+
diff --git a/Source/Serrano/graph/computation_graph.swift b/Source/Serrano/graph/computation_graph.swift
new file mode 100644
index 0000000..ea27c7e
--- /dev/null
+++ b/Source/Serrano/graph/computation_graph.swift
@@ -0,0 +1,708 @@
+//
+// computation_graph.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 8/7/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+
+/**
+## Intro
+The implementation of `Graph`.
+A `ComputationGraph` just a set of symbols and connections between them.
+The graph computes the output tensors stage by stage.
+
+A typical usage:
+
+```swift
+
+let graph = ComputationGraph()
+
+let a = graph.tensor("A", shape: TensorShape(dataType: .float, shape: [2,3]))
+let b = graph.tensor("B", shape: TensorShape(dataType: .float, shape: [2,3]))
+let (c, op) = graph.operation("", inputSymbols: [a, b], op: PowOperator()).first
+```
+
+## ComputationGraph V.S. Model
+`ComputationGraph` is low-level abstraction of machine learning models.
+It has two basic funtions:
+ - forward. Compute results from input to output
+ - backward. Compute and update data symbols that are differentiable use optimizer
+User needs to call `forward(:)` and `backward(:)` manually to do training and `ComputationGraph`
+is not aware of _loss function_.
+If you want to use loss function if a graph, you need add it into this graph as an operator symbol.
+
+`Model` is a higher level abstraction inherited from `ComputationGraph`.
+It has all functions `ComputationGraph` has and beyond that:
+ - Loss function. `Model` could setup loss function to do the backward training.
+ 'ComputationGraph'
+ - High level training functions. `Model` could automatically repeat `forward(:)` and `backward(:)` util
+ reaches conditions users setup like max number of epoch, early stop etc.
+ - High level prediction functions.
+*/
+public class ComputationGraph: Graph {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// Conforms to `Graph`
+ public var symbols: [String: GraphSymbol]
+
+ /// Conforms to `Graph`
+ public var graphLabel: String
+
+ /// Conforms to `Graph`.
+ /// Default is `false`.
+ public var trainable: Bool = false
+
+ /// Description
+ public var description: String {
+ get {
+ return "ComputationGraph(\(self.graphLabel))"
+ }
+ }
+
+ /// Optimizer of this graph doing backward training.
+ /// Could be `nil` if just do forward calcualtion.
+ public var optimizer: Optimizer? = nil
+
+ /// This attribute indicates if the graph has been sorted.
+ /// Initial value is `false`.
+ public var sorted: Bool = false
+
+ /// A dictionary stores the sorted symbols after applying topology sorting.
+ /// The key is the depth value, and the value is the list of symbols in this depth stage.
+ public var symbolStages: [Int: [GraphSymbol]] = [Int: [GraphSymbol]]()
+
+ /// Counter of backward training
+ public var epoch: Int {
+ get {
+ return self._epoch
+ }
+ }
+
+ /// Counter of backward training
+ internal var _epoch: Int = 0
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+ /// Designated init
+ ///
+ /// - Parameters:
+ /// - symbols: symbols
+ /// - graphLabel: graphLabel
+ /// - trainable: trainable
+ public init(symbols: [String: SerranoGraphSymbol], graphLabel: String, trainable: Bool) {
+ self.symbols = symbols
+ self.graphLabel = graphLabel
+ self.trainable = trainable
+ }
+
+ /// Convenience init
+ ///
+ /// - Parameter graphLabel: graphLabel
+ public convenience init(_ graphLabel: String? = nil) {
+ if graphLabel != nil {
+ self.init(symbols: [String : SerranoGraphSymbol](), graphLabel: graphLabel!, trainable: false)
+ } else {
+ self.init(symbols: [String : SerranoGraphSymbol](), graphLabel: "Serrano Graph", trainable: false)
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Util
+
+ /// Generate a default label for a symbol.
+ ///
+ /// - Parameter type: SymbolType
+ /// - Returns: return value
+ public func defaultSymbolLabel(_ type: SymbolType) -> String {
+ return "Serrano Graph"
+ }
+
+ /// Get this graph's all data symbols
+ ///
+ /// - Returns: list of data symbol
+ public func dataSymbols() -> [DataSymbol] {
+ return self.symbols.filter {$0.value.symbolType.isDataSymbol()}.map {$0.value as! DataSymbol}
+ }
+
+
+ /// Get his graph's all operator symbols
+ ///
+ /// - Returns: list of operator symbols
+ public func opSymbols() -> [OperatorSymbol] {
+ return self.symbols.filter {$0.value.symbolType == SymbolType.Operator}.map {$0.value as! OperatorSymbol}
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods conforms to `Graph`
+
+ /// Add a `TensorSymbol` to the graph.
+ ///
+ /// - Parameter label: label
+ /// - Returns: A `TensorSymbol`
+ @discardableResult
+ public func tensor(_ label: String? = nil, shape: TensorShape) -> TensorSymbol {
+ // new symbol, graph unsorted
+ self.sorted = false
+
+ var symbolLabel = label
+ if symbolLabel == nil { symbolLabel = self.defaultSymbolLabel(.Tensor) }
+ let tensorSymbol = SerranoTensorSymbol(symbolLabel!, dataSource: .User, shape: shape)
+ self.symbols[tensorSymbol.UID] = tensorSymbol
+ // logging
+ SerranoLogging.stdLogging(message: "New TensorSymbol added to graph [\(self.graphLabel)]",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)",
+ loggingLevel: SerranoLoggingType.Regular)
+
+ return tensorSymbol
+ }
+
+ /// Add a `ScalarSymbol` to the graph.
+ ///
+ /// - Parameter label: label
+ /// - Returns: A `ScalarSymbol`
+ @discardableResult
+ public func scalar(_ label: String? = nil, dataType: TensorDataType) -> ScalarSymbol {
+ // new symbol, graph unsorted
+ self.sorted = false
+
+ var symbolLabel = label
+ if symbolLabel == nil { symbolLabel = self.defaultSymbolLabel(.Scalar) }
+ let scalarSymbol = SerranoScalarSymbol(symbolLabel!, dataType: dataType, dataSource: .User)
+ self.symbols[scalarSymbol.UID] = scalarSymbol
+ // logging
+ SerranoLogging.stdLogging(message: "New ScalarSymbol added to graph [\(self.graphLabel)]",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)",
+ loggingLevel: SerranoLoggingType.Regular)
+
+ return scalarSymbol
+ }
+
+ /// Add a `OperatorSymbol` to the graph.
+ /// This function will also update inBounds and outBounds information for involving symbols.
+ ///
+ /// - Parameters:
+ /// - inputs: input array of `TensorSymbol`
+ /// - operator: A `ComputableOperator` instance
+ /// - Returns:
+ /// - outputTensorSymbols: output tensor symbols
+ /// - operatorSymbol: added operator symbol
+ /// - paramSymbols: Parameter symbols attached to this operator. Empty array if not available.
+ @discardableResult
+ public func operation(_ label: String? = nil, inputs: [TensorSymbol], op: ComputableOperator) -> (outputTensorSymbols: [TensorSymbol], operatorSymbol: OperatorSymbol, paramSymbols: [GraphSymbol]) {
+ // new symbol, graph unsorted
+ self.sorted = false
+
+ var symbolLabel = label
+ if symbolLabel == nil { symbolLabel = self.defaultSymbolLabel(.Scalar) }
+ let opSymbol = SerranoOperatorSymbol(symbolLabel!, serranoOperator: op, inputSymbols: inputs)
+ self.addSymbols(opSymbol)
+
+ // logging
+ SerranoLogging.stdLogging(message: "New OperatorSymbol added to graph [\(self.graphLabel)]",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)",
+ loggingLevel: SerranoLoggingType.Regular)
+
+ // output symbols bounds process
+ let outTensorSymbols = opSymbol.outputSymbols()
+ for outputSymbol in outTensorSymbols {
+ let symbol = outputSymbol as GraphSymbol?
+ symbol!.addToInBound(opSymbol)
+
+ opSymbol.addToOutBound(outputSymbol)
+
+ self.addSymbols(outputSymbol)
+ }
+
+ // inputs symbols bounds process
+ for inputSymbol in inputs {
+ let symbol = inputSymbol as GraphSymbol?
+ symbol!.addToOutBound(opSymbol)
+
+ self.addSymbols(inputSymbol)
+ }
+
+ // param symbols
+ let paramSymbols = op.paramSymbols()
+ for paramSymbol in paramSymbols {
+ let symbol = paramSymbol as GraphSymbol?
+ symbol!.addToOutBound(opSymbol)
+
+ // add to opSymbol
+ opSymbol.addToInBound(paramSymbol)
+ opSymbol.addToParamSymbols(paramSymbol)
+
+ self.addSymbols(paramSymbol)
+ }
+
+ return (outTensorSymbols, opSymbol, paramSymbols)
+ }
+
+ /// Bind data to `TensorSymbol` or `ScalarSymbol` which need feeding data from users.
+ /// This function will try to bind every entry in `data` to tensor or scalar symbol in `symbols`.
+ /// If could find an entry has same UID in `symbols` for passed in data, it will be ignored.
+ ///
+ /// - Note: This function does not verify any restrictions.
+ ///
+ /// - Parameter data: A dictinary whose key is `UID` of a symbol
+ /// and the value is a `DataSymbolSupportedDataType` object.
+ public func bindData(_ data: [String: DataSymbolSupportedDataType]) {
+ for (UID, entry) in data {
+ let symbol = self.symbols[UID]
+ // not find
+ if symbol == nil {
+ SerranoLogging.warningLogging(message: "Could not find symbol with UID (\(UID)) in \(self.description).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ continue
+ }
+
+ guard symbol! is DataSymbol else {
+ SerranoLogging.errorLogging(message: "Trying to bind data to symbol \(symbol!), but this is not a data symbol",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("Error raised by Serrano. Check log for details.")
+ }
+
+ guard (symbol! as! DataSymbol).bindData(entry) else {
+ SerranoLogging.errorLogging(message: "Trying to bind data to symbol \(symbol!), but failed. Check log for details.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("Error raised by Serrano. Check log for details.")
+ }
+ }
+ }
+
+ /// Compute the whole graph from inputs to outputs.
+ /// If there's any error during calculation or pre-checking, return `nil`.
+ ///
+ /// - Parameter mode: computation mode
+ /// - Returns: Array of tensors/Scalar or `nil` if error happens
+ public func forward(mode: OperatorComputationMode) -> [DataSymbolSupportedDataType]? {
+ // compute in stage order
+ self.stageOrderCalculate(mode: mode)
+
+ // group last stage tensors and return
+ let lastStage = self.symbolStages.keys.max()!
+ let outputDataSymbols = self.symbolStages[lastStage]?.filter { $0.symbolType.isDataSymbol()}
+
+ guard outputDataSymbols != nil else {
+ SerranoLogging.errorLogging(message: "Could not gather last stage tensors. Maybe graph defined is not valid.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ return (outputDataSymbols as! [DataSymbol]).map {$0.bindedData!}
+ }
+
+ /// Backward computing the grads for updatable data symbols.
+ ///
+ /// This function supposes that graph already been verifed and done at least onece forward computation.
+ /// If not, it may cause unexpected error.
+ ///
+ /// - Note: If this graph is not `trainable`, `fatalError` will be raised.
+ ///
+ /// - Parameters:
+ /// - mode: computation mode
+ public func backward(mode: OperatorComputationMode) {
+ // update parameters
+ self.dataSymbolsUpdate(mode)
+
+ // windup
+ self.windup()
+
+ // increase epoch
+ self._epoch += 1
+ SerranoLogging.stdLogging(message: "Finish epoch \(self._epoch).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: SerranoLoggingType.LowLevel)
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Forawrd related methods
+
+
+ /// Prepare before forwarding.
+ /// Usually should be called just before 1st fowarding.
+ /// However, if user chagne graph strcture, this should be called again.
+ public func forwardPrepare() {
+ // sort
+ self.sortGraph()
+
+ // user input bind check
+ var (valid, msg) = self.userInputBindCheck()
+ guard valid else {
+ SerranoLogging.errorLogging(message: "Some symbols havn't been binded. Detail:\(msg)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+
+ // allocate tensors for needs
+ self.allocateTensors()
+
+ // verify
+ (valid, msg) = self.verifyGraph()
+ guard valid else {
+ SerranoLogging.errorLogging(message: "Graph is not valid. Detail:\(msg)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ }
+
+ /// Use topology sorting to sort all symbols into different stages.
+ /// Store results in `symbolStages`.
+ /// Currently, only supports __directed acyclic graph__ (i.e. no directed cycles).
+ ///
+ /// - Note: If there is no input data symbols, `fatalError` will be raised.
+ ///
+ /// # Algorithm
+ /// Use DFS(Depth-First Search) to construct the staged information.
+ ///
+ /// 1. For each data symbol whose `dataSource` is `SymbolDataSource.User`:
+ ///
+ /// I. Add this symbol to a list with depth 0.
+ ///
+ /// II. Use DFS to starting from this symbol following outBounds path,
+ /// and add the visiting symbol and corresponding depth into list.
+ /// Each time gos deeper, depth plus 1.
+ ///
+ /// III. If the visiting symbol is already in the statck, set depth to max(this visiting depth, existing depth).
+ /// 2. Then according the list information, add symbols in same stage into `symbolStages`.
+ public func sortGraph() {
+ // get input data symbols
+ let inputDataSymbols = self.symbols.filter { (UID, symbol) -> Bool in
+ return symbol.symbolType.isDataSymbol() && symbol.inBounds.count == 0
+ }
+
+ guard inputDataSymbols.count > 0 else {
+ SerranoLogging.errorLogging(message: "Could not found input data symbols in this graph (\(self.graphLabel))." +
+ "Thus cannot generate computation graph.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+
+ // DFS
+ var symbolDepthList = [SerranoGraphSymbol: Int]() // key is UID
+ for (_, symbol) in inputDataSymbols {
+ self.visitSymbol(symbol, symbolDepthList: &symbolDepthList, currentDepth: 0)
+ }
+
+ // add stage information
+ for (symbol, depth) in symbolDepthList {
+ if self.symbolStages[depth] == nil { self.symbolStages[depth] = [SerranoGraphSymbol]() }
+ self.symbolStages[depth]!.append(symbol)
+ }
+
+ // set attribute
+ self.sorted = true
+ }
+
+ /// Visist a symbol and its outBounds symbols in DFS manner.
+ ///
+ /// - Parameters:
+ /// - symbol: visiting symbol
+ /// - symbolDepthList: symbolDepthList. The passed in dictionary will be mutated.
+ /// - currentDepth: current visiting depth
+ public func visitSymbol(_ symbol: GraphSymbol, symbolDepthList: inout [SerranoGraphSymbol: Int], currentDepth: Int) {
+ let serranoSymbol = symbol as! SerranoGraphSymbol
+ if symbolDepthList[serranoSymbol] == nil {
+ symbolDepthList[serranoSymbol] = currentDepth
+ } else {
+ symbolDepthList[serranoSymbol] = max(symbolDepthList[serranoSymbol]!, currentDepth)
+ }
+
+ for outSymbol in symbol.outBounds {
+ self.visitSymbol(outSymbol, symbolDepthList: &symbolDepthList, currentDepth: currentDepth + 1)
+ }
+ }
+
+ /// This functions verifies:
+ /// - All symbols should have been binded to data
+ /// - Shape compatibility between connected operators and tensors
+ ///
+ /// If any incorrectness found, return `false` and associated message.
+ ///
+ /// - Note: This function assumes graph already been sorted and attribute `symbolStages` already has stage info.
+ ///
+ /// - Returns: `valid` represent if passing the verification and error msg if has
+ public func verifyGraph() -> (valid: Bool, msg: String) {
+ // check all data symbols have been binded
+ for (_, symbol) in self.symbols {
+ if symbol.symbolType == .Scalar {
+ let scalarSymbol = symbol as! ScalarSymbol
+ guard scalarSymbol.bindedData != nil else {
+ return (false, "ScalarSymbol(\(scalarSymbol.symbolLabel)) needs user feeded data but bindedData is nil.")
+ }
+ } else if symbol.symbolType == .Tensor {
+ let tensorSymbol = symbol as! TensorSymbol
+ guard tensorSymbol.bindedData != nil else {
+ return (false, "TensorSymbol(\(tensorSymbol.symbolLabel)) needs user feeded data but bindedData is nil.")
+ }
+ }
+ }
+
+ // check if sorted
+ guard self.sorted else { return (false, "Graph has not been sorted.") }
+
+ // check shape matching
+ let (valid, msg) = self.checkShapeChain()
+ guard valid else { return (false, "\(msg)") }
+
+ return (true, "")
+ }
+
+ /// This function check a sorted graph's every path to see if all connected operators
+ /// and symbols could match each other considering `TensorShape`.
+ ///
+ /// ## Checking
+ /// ```
+ /// For each operator symbol in this stage:
+ /// I. Get input tensors from inBounds and output tensors from outBounds.
+ /// Assign tensors to operator's inputTensors and outputTensors.
+ /// II. Call inputOutputTensorCheck().
+ /// a). If return true, mark operator's disableInputOutputCheck to true.
+ /// Continue.
+ /// b). If return false, return false and related errror msg.
+ /// ```
+ ///
+ /// - Note: This internal function assumes the graph's all symbols have been binded.
+ ///
+ /// - Returns: validation result and error message if has
+ public func checkShapeChain() -> (valid: Bool, msg: String) {
+ let opSymbols = self.symbols.filter { (UID, symbol) -> Bool in
+ return symbol.symbolType == SymbolType.Operator
+ }
+
+ for (_, symbol) in opSymbols {
+ var opSymbol = symbol as! OperatorSymbol
+
+ // setup input tensors
+ // Note here should use `inputSymbols` not `inBounds`.
+ // Cause `inBounds` also includes `paramSymbols`.
+ let inputTensors = opSymbol.inputSymbols.map { $0.bindedData! }
+ opSymbol.serranoOperator.inputTensors = (inputTensors as! [Tensor])
+
+ // setup output tensors
+ let outputTensors = opSymbol.outBounds.map { ($0 as! TensorSymbol).bindedData! }
+ opSymbol.serranoOperator.outputTensors = (outputTensors as! [Tensor])
+
+ // bind parameter
+ let paramSymbols = opSymbol.inBounds.filter({ (symbol) -> Bool in
+ // not in input
+ return !opSymbol.inputSymbols.contains { $0.UID == symbol.UID}
+ })
+ opSymbol.serranoOperator.bindParamSymbols(paramSymbols)
+
+ let (pass, msg) = opSymbol.serranoOperator.inputOutputTensorsCheck()
+ guard pass else {
+ return (false, "Operator symbol \(opSymbol.symbolLabel) failed to pass inputOutputTensorsCheck(). " +
+ "Details: \(msg)")
+ }
+ }
+
+ return (true, "")
+ }
+
+ /// Check if all user data source symbols have been binded.
+ ///
+ /// - Returns: if pass checking and related error msg if has.
+ public func userInputBindCheck() -> (valid: Bool, msg: String) {
+ let dataSymbols = self.symbols.filter { $0.value.symbolType.isDataSymbol() }
+ for (_, symbol) in dataSymbols {
+ switch symbol.symbolType {
+ case .Tensor:
+ let tensorSymbol = symbol as! TensorSymbol
+ if tensorSymbol.dataSource == .User {
+ guard tensorSymbol.bindedData != nil else {
+ return (false, "Tensor symbol (\(tensorSymbol.symbolLabel)) should bind data feeding from user but is nil.")
+ }
+ }
+ case .Scalar:
+ let scarlarSymbol = symbol as! ScalarSymbol
+ if scarlarSymbol.dataSource == .User {
+ guard scarlarSymbol.bindedData != nil else {
+ return (false, "Scalar symbol (\(scarlarSymbol.symbolLabel)) should bind data feeding from user but is nil.")
+ }
+ }
+ default:
+ continue
+ }
+ }
+
+ return (true, "")
+ }
+
+
+ /// Allocate tensors for all tensor symbols.
+ /// This is used when training a network from zero.
+ ///
+ /// - Note: This method ignore existing binding, allocate new tensors for all tensor symbols.
+ public func allocateAllTensors() {
+ for (_, symbol) in self.symbols {
+ if symbol.symbolType == .Tensor {
+ var tensorSymbol = symbol as! TensorSymbol
+ tensorSymbol.bindedData = SerranoResourceManager.globalManager.allocateUnamangedTensor(tensorSymbol.shape)
+ }
+ }
+ }
+
+ /// Allocate tensors for all tensor symbols not feeding from users.
+ /// Currently, user `SerranoResourceManager.globalManager` to allocate unmanaged resources.
+ ///
+ /// TODO: Do memory dependency analize, reuser tensors.
+ public func allocateTensors() {
+ for (_, symbol) in self.symbols {
+ if symbol.symbolType == .Tensor {
+ var tensorSymbol = symbol as! TensorSymbol
+
+ if tensorSymbol.dataSource == .User || tensorSymbol.bindedData != nil {
+ continue
+ } else {
+ tensorSymbol.bindedData = SerranoResourceManager.globalManager.allocateUnamangedTensor(tensorSymbol.shape)
+ }
+ }
+ }
+ }
+
+
+ /// Stage by stage, run all operators.
+ ///
+ /// ## Algorithm
+ /// ````
+ /// for stage i in [0, n]:
+ /// run all operators in stage i simutaneously
+ /// ```
+ ///
+ /// - Note: Graph should have been sorted. Else `fatalError` will be raised.
+ internal func stageOrderCalculate(mode: OperatorComputationMode) {
+ // check sorted
+ guard self.sorted else {
+ SerranoLogging.errorLogging(message: "Graph not sorted. Abort calculation.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+
+ let stageWorkGroup = DispatchGroup()
+ let begginTime = CFAbsoluteTimeGetCurrent()
+ for stage in self.symbolStages.keys.sorted() {
+ for symbol in self.symbolStages[stage]! {
+ if symbol.symbolType == .Operator {
+ var opSymbol = symbol as! OperatorSymbol
+ stageWorkGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ opSymbol.serranoOperator.disableInputOutputCheck = true
+ opSymbol.serranoOperator.compute(mode)
+ stageWorkGroup.leave()
+ }
+ }
+ }
+ // wait all complete in this stage
+ stageWorkGroup.wait()
+ }
+
+ SerranoLogging.stdLogging(message: "Finish forward for graph \(self.graphLabel) in \(CFAbsoluteTimeGetCurrent() - begginTime) seconds ",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: SerranoLoggingType.LowLevel)
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Backward related methods
+
+
+ /// Prepare workd.
+ internal func backwardPrepare() {
+ guard self.trainable else {
+ SerranoLogging.errorLogging(message: "Graph is not trainable.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("")
+ }
+
+ // check opmizer not nil
+ guard self.optimizer != nil else {
+ SerranoLogging.errorLogging(message: "Optimizer of graph is nil. Cannot do backward training.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("")
+ }
+
+ // initialize grad value tensors at the very first training
+ if self._epoch == 0 {
+ self.allocateTensorsForGrads()
+ }
+
+ // let optimizer do prepare work if need
+ self.optimizer!.prepare(self)
+ }
+
+ /// Allocate tensors and initialize scalar values for grads of data symbol.
+ ///
+ /// ## Initialization strategy
+ /// We will only look at operators with `true` values for attribute `enabledParameterUpdate`,
+ /// and will initialize currentGrad of these operator symbols' inbound data symbols with
+ /// `true` value for attribute `updateble`.
+ internal func allocateTensorsForGrads() {
+ for opSymbol in self.opSymbols() {
+ if opSymbol.enabledParameterUpdate {
+ for dataSymbol in opSymbol.inBounds {
+ if dataSymbol.symbolType.isDataSymbol() {
+ switch(dataSymbol.symbolType) {
+ case SymbolType.Scalar:
+ var scalarSymbol = dataSymbol as! ScalarSymbol
+ scalarSymbol.currentGrad = Float(0.0)
+ case SymbolType.Tensor:
+ var tensorSymbol = dataSymbol as! TensorSymbol
+ tensorSymbol.currentGrad = SerranoResourceManager.globalManager.allocateUnamangedTensor(tensorSymbol.shape)
+ default:
+ continue
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /// This function do updating during backward training.
+ ///
+ /// For operator symbols at each stage starting from last to first,
+ /// calculate the grads for all inbound data symbols.
+ /// Then update values for those are `updateble`.
+ internal func dataSymbolsUpdate(_ mode: OperatorComputationMode) {
+ let workGroup = DispatchGroup()
+ for (_, symbols) in (self.symbolStages.sorted {$0.0.key > $0.1.key}) { // desending
+ for symbol in (symbols.filter {$0.symbolType == SymbolType.Operator}) {
+ workGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ let opSymbol = symbol as! OperatorSymbol
+ // TODO: Improve API design.
+ // In this function operator will allocate new tensors/scalars holding grads, which is not good.
+ // The idea is that graph itself do all memory allocation, so later we can do optimization.
+ let gradsInfo = opSymbol.serranoOperator.gradCompute(mode)
+ for (label, gradValue) in gradsInfo {
+ // get corresponding inpbound data symbol
+ var inboundSymbol = opSymbol.inboundSymbolForGradLabel(label)
+ guard inboundSymbol != nil else {
+ SerranoLogging.errorLogging(message: "Unexpexted nil object.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("")
+ }
+
+ if inboundSymbol!.updatable {
+ // up grads
+ let gradRelatedOutSymbols = opSymbol.gradRelatedOutputSymbols(onInboundSymbol: inboundSymbol!)
+ let upGrads = gradRelatedOutSymbols.flatMap {$0.currentGrad} // take out nil
+
+ inboundSymbol!.backwardUpdate(self.optimizer!, gradValue: gradValue, upGrads: upGrads)
+ }
+ }
+ workGroup.leave()
+ }
+ }
+ }
+ workGroup.wait()
+ }
+
+ /// This function does windup work after updating all grads in this graph.
+ internal func windup() {
+
+ }
+
+}
+
diff --git a/Source/Serrano/graph/symbols/graph_symbol.swift b/Source/Serrano/graph/symbols/graph_symbol.swift
new file mode 100644
index 0000000..575ffa6
--- /dev/null
+++ b/Source/Serrano/graph/symbols/graph_symbol.swift
@@ -0,0 +1,142 @@
+//
+// graph_symbol.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 8/7/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+
+typealias WeakSerranoGraphSymbol = WeakRef
+
+/**
+Implementation of `GraphSymbol`
+*/
+public class SerranoGraphSymbol: GraphSymbol, Hashable {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// Symbol type. Conforms to `GraphSymbol`.
+ public var symbolType: SymbolType
+
+ /// Unique symbol ID. Conforms to `GraphSymbol`.
+ /// - Note: A 6-length `String` consists of `[a-zA-Z0-9]`
+ public var UID: String
+
+ /// Readable label. Conforms to `GraphSymbol`.
+ public var symbolLabel: String
+
+ /// Inbound symbols list. Conforms to `GraphSymbol`.
+ /// To prevent from cycle reference, we dynamic constructing this attribute from `inBoundsWeak`.
+ public var inBounds: [GraphSymbol] {
+ get {
+ return self.inBoundsWeak.filter {$0.value != nil}.map {$0.value!}
+ }
+ set(bounds) {
+ for symbol in bounds {
+ self.addToInBound(symbol)
+ }
+ }
+ }
+
+ /// Outbound symbols list. Conforms to `GraphSymbol`.
+ /// To prevent from cycle reference, we dynamic constructing this attribute from `inBoundsWeak`.
+ public var outBounds: [GraphSymbol] {
+ get {
+ return self.outBoundsWeak.filter {$0.value != nil}.map {$0.value!}
+ }
+ set(bounds) {
+ for symbol in bounds {
+ self.addToOutBound(symbol)
+ }
+ }
+ }
+
+ /// Hash value.
+ /// Conforms to `equatable` protocol.
+ public var hashValue: Int {
+ get {
+ return self.UID.hashValue
+ }
+ }
+
+ /// Weak reference array of inbounds objects
+ internal var inBoundsWeak: [WeakSerranoGraphSymbol]
+
+ /// Weak reference array of outbounds objects
+ internal var outBoundsWeak: [WeakSerranoGraphSymbol]
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+ /// Designated init
+ ///
+ /// - Parameters:
+ /// - symbolType: symbolType
+ /// - label: label
+ /// - inBounds: inBounds
+ /// - outBounds: outBounds
+ public init(label: String, symbolType: SymbolType,
+ inBounds: [GraphSymbol], outBounds: [GraphSymbol]) {
+ self.symbolType = symbolType
+ self.symbolLabel = label
+ self.inBoundsWeak = [WeakSerranoGraphSymbol]()
+ self.outBoundsWeak = [WeakSerranoGraphSymbol]()
+ self.UID = serranoSymbolUIDGenerate()
+
+ // add
+ for symbolIn in inBounds {
+ self.addToInBound(symbolIn)
+ }
+ for symbolOut in outBounds {
+ self.addToOutBound(symbolOut)
+ }
+ }
+
+ /// Convenience init
+ ///
+ /// - Parameters:
+ /// - symbolType: symbolType
+ /// - label: label
+ public convenience init(_ label: String = "", symbolType: SymbolType) {
+ self.init(label: label, symbolType: symbolType, inBounds: [GraphSymbol](), outBounds: [GraphSymbol]())
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+ public func evaluate() -> [DataSymbolSupportedDataType]? {
+ fatalError("Should not be called.")
+ }
+
+ /// Add new symbol to `inBounds`.
+ /// Should check duplicate.
+ public func addToInBound(_ symbol: GraphSymbol) {
+ let s = symbol as! SerranoGraphSymbol
+ if !(self.inBoundsWeak.contains {$0.value == s}) {
+ let weakSymbol = WeakSerranoGraphSymbol(value: s)
+ self.inBoundsWeak.append(weakSymbol)
+ }
+ }
+
+ /// Add new symbol to `outBounds`
+ /// Should check duplicate.
+ public func addToOutBound(_ symbol: GraphSymbol) {
+ let s = symbol as! SerranoGraphSymbol
+ if !(self.outBoundsWeak.contains {$0.value == s}) {
+ let weakSymbol = WeakSerranoGraphSymbol(value: s)
+ self.outBoundsWeak.append(weakSymbol)
+ }
+ }
+
+ /// Conforms to `equatable` protocol
+ ///
+ /// - Parameters:
+ /// - lhs: left compare
+ /// - rhs: right compare
+ /// - Returns: return value
+ public static func == (lhs: SerranoGraphSymbol, rhs: SerranoGraphSymbol) -> Bool {
+ return lhs.UID == rhs.UID
+ }
+}
diff --git a/Source/Serrano/graph/symbols/operator_symbol.swift b/Source/Serrano/graph/symbols/operator_symbol.swift
new file mode 100644
index 0000000..64bbffd
--- /dev/null
+++ b/Source/Serrano/graph/symbols/operator_symbol.swift
@@ -0,0 +1,92 @@
+//
+// operator_symbol.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 8/7/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+
+
+
+/**
+Implementaion of `OperatorSymbol`
+*/
+public class SerranoOperatorSymbol: SerranoGraphSymbol, OperatorSymbol {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// Operator instance
+ public var serranoOperator: ComputableOperator
+
+ public var inputTensorShapes: [TensorShape]
+
+ /// Input tensor symbols
+ public var inputSymbols: [TensorSymbol]
+
+ /// Params tensor symbols
+ public var paramSymbols: [DataSymbol]
+
+ /// Control if update asscoiated operator's parameter
+ /// Default is `false`
+ public var enabledParameterUpdate = false
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+ /// Designated initalizers
+ ///
+ /// - Parameters:
+ /// - serranoOperator: serranoOperator description
+ /// - label: label description
+ /// - inBounds: inBounds description
+ /// - outBounds: outBounds description
+ public init(_ label: String = "Operator symbol",
+ serranoOperator: ComputableOperator,
+ inputSymbols: [TensorSymbol],
+ paramSymbols: [DataSymbol]? = nil,
+ inBounds: [GraphSymbol] = [GraphSymbol](),
+ outBounds: [GraphSymbol] = [GraphSymbol]()) {
+ self.serranoOperator = serranoOperator
+ self.inputSymbols = inputSymbols
+
+ if paramSymbols == nil {
+ self.paramSymbols = [DataSymbol]()
+ } else {
+ self.paramSymbols = paramSymbols!
+ }
+
+ self.inputTensorShapes = inputSymbols.map {$0.shape}
+ super.init(label: label, symbolType: .Operator, inBounds: inputSymbols, outBounds: [GraphSymbol]())
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+ /// Get output symbols of this operator.
+ /// The output shapes are decided by operaotr's function `outputShape(shapeArray: inputShapes)`
+ ///
+ /// - Note: This function will automatically add return symbols in `outBounds`.
+ ///
+ /// - Returns: Array of TensorSymbols.
+ public func outputSymbols() -> [TensorSymbol] {
+ var output = [TensorSymbol]()
+
+ let inputShapes = self.inputSymbols.map { $0.shape }
+ let outputShapes = self.serranoOperator.outputShape(shapeArray: inputShapes)
+ guard outputShapes != nil else {
+ SerranoLogging.errorLogging(message: "Operator symbol (\(self.symbolLabel)) has invalid tensor symbols. It could not generate output tensor symbols. Check previous log for detail error info.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+
+ for outShape in outputShapes! {
+ output.append(SerranoTensorSymbol(dataSource: .Calculation, shape: outShape))
+ self.addToOutBound(output.last! as GraphSymbol)
+ }
+
+ return output
+ }
+}
+
diff --git a/Source/Serrano/graph/symbols/scalar_symbol.swift b/Source/Serrano/graph/symbols/scalar_symbol.swift
new file mode 100644
index 0000000..4165b4e
--- /dev/null
+++ b/Source/Serrano/graph/symbols/scalar_symbol.swift
@@ -0,0 +1,108 @@
+//
+// scalar_symbol.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 8/7/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+
+
+/**
+Implementation of `ScalarSymbol`
+*/
+public class SerranoScalarSymbol: SerranoGraphSymbol, ScalarSymbol {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// Binded scalar value
+ public var bindedData: DataSymbolSupportedDataType? {
+ get {
+ return self._bindedData as? DataSymbolSupportedDataType
+ }
+ set(newValue) {
+ if newValue is SupportedScalarDataType? {
+ self._bindedData = newValue as! SupportedScalarDataType?
+ } else {
+ SerranoLogging.errorLogging(message: "Unexpexted data type. Expecte scalar type.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("")
+ }
+ }
+ }
+
+ /// The data type
+ public var dataType: TensorDataType
+
+ /// Data source
+ public var dataSource: SymbolDataSource
+
+ /// If differentiable
+ public var updatable = false
+
+ /// Current grad
+ public var currentGrad: DataSymbolSupportedDataType?
+
+ /// If enabled history grads recording.
+ /// Default is `false`.
+ public var historyGradsEnabled = false
+
+ /// grads
+ public var historyGrads: [DataSymbolSupportedDataType] = [SupportedScalarDataType]() as! [DataSymbolSupportedDataType]
+
+ /// Scalar value
+ internal var _bindedData: SupportedScalarDataType?
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+ /// Designated init
+ ///
+ /// - Parameters:
+ /// - dataType: dataType
+ /// - dataSource: dataSource
+ /// - bindedData: bindedData
+ /// - label: label
+ /// - inBounds: inBounds
+ /// - outBounds: outBounds
+ public init(dataType: TensorDataType, dataSource: SymbolDataSource, bindedData: SupportedScalarDataType? = nil,
+ label: String, inBounds: [GraphSymbol], outBounds: [GraphSymbol]) {
+ self.dataType = dataType
+ self._bindedData = bindedData
+ self.dataSource = dataSource
+ super.init(label: label, symbolType: .Scalar, inBounds: inBounds, outBounds: outBounds)
+ }
+
+ /// Convenience init
+ ///
+ /// - Parameter dataType: dataType
+ public convenience init(_ label: String = "Scalar Symbol", dataType: TensorDataType, dataSource: SymbolDataSource) {
+ self.init(dataType: dataType, dataSource: dataSource, bindedData: nil, label: label, inBounds: [GraphSymbol](), outBounds: [GraphSymbol]())
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+ /// Bind to a scalar variable
+ ///
+ /// - Note: If passed in `data`'s type is uncompatible with attribute `dataType`, no further action will be taken.
+ /// Since when doing calculation, all data converted to `Float`.
+ ///
+ ///
+ /// - Parameter data: data. Should be a scalar variable (`Int`, 'Float' or `Double`).
+ /// - Returns: bind result
+ @discardableResult
+ public func bindData(_ data:DataSymbolSupportedDataType) -> Bool {
+ // check data type
+ guard (data is SupportedScalarDataType) else {
+ SerranoLogging.errorLogging(message: "Scalar symbol (\(self.symbolLabel)) expects a scalar object to bind. Given \(type(of: data)).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return false
+ }
+
+ self._bindedData = data as! SupportedScalarDataType
+
+ return true
+ }
+}
diff --git a/Source/Serrano/graph/symbols/tensor_symbol.swift b/Source/Serrano/graph/symbols/tensor_symbol.swift
new file mode 100644
index 0000000..04eb73a
--- /dev/null
+++ b/Source/Serrano/graph/symbols/tensor_symbol.swift
@@ -0,0 +1,115 @@
+//
+// tensor_symbol.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 8/7/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+
+
+/**
+Implementation of `TensorSymbol`
+*/
+public class SerranoTensorSymbol: SerranoGraphSymbol, TensorSymbol {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// Data source. Conforms to `GraphSymbol`.
+ public var dataSource: SymbolDataSource
+
+ /// Shape. Conforms to `GraphSymbol`.
+ public var shape: TensorShape
+
+ /// Binded data. Conforms to `GraphSymbol`.
+ public var bindedData: DataSymbolSupportedDataType? {
+ get {
+ return self._bindedData
+ }
+ set(newValue) {
+ if newValue is Tensor? {
+ self._bindedData = newValue as! Tensor?
+ } else {
+ SerranoLogging.errorLogging(message: "Unexpexted data type. Expect Tensor object",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("")
+ }
+ }
+ }
+
+ /// If differentiable
+ public var updatable = false
+
+ /// Current grad
+ public var currentGrad: DataSymbolSupportedDataType?
+
+ /// If enabled history grads recording.
+ /// Default is `false`.
+ public var historyGradsEnabled = false
+
+ /// grads
+ public var historyGrads:[DataSymbolSupportedDataType] = [Tensor]()
+
+ /// Binded tensor
+ internal var _bindedData: Tensor?
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+ /// Designated init
+ ///
+ /// - Parameters:
+ /// - dataSource: dataSource
+ /// - bindedData: bindedData
+ /// - label: label
+ /// - inBounds: inBounds
+ /// - outBounds: outBounds
+ public init(dataSource: SymbolDataSource, bindedData: Tensor?, shape: TensorShape,
+ label: String, inBounds: [GraphSymbol], outBounds: [GraphSymbol]) {
+ self.dataSource = dataSource
+ self._bindedData = bindedData
+ self.shape = shape
+ super.init(label: label, symbolType: .Tensor, inBounds: inBounds, outBounds: outBounds)
+ }
+
+ /// Covenient init
+ ///
+ /// - Parameters:
+ /// - label: label
+ /// - dataSource: dataSource
+ public convenience init(_ label: String = "Tensor Symbol", dataSource: SymbolDataSource, shape: TensorShape) {
+ self.init(dataSource: dataSource, bindedData: nil, shape: shape,
+ label: label, inBounds: [GraphSymbol](), outBounds: [GraphSymbol]())
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+ /// Bind to a tensor.
+ /// There two cases could not bind successfull:
+ /// 1. Passed in `data` is not a tensor object;
+ /// 2. Passed in tensor object has not compatible shape with attribute `shape`.
+ ///
+ /// - Parameter data: data. Should be a `Tensor` object.
+ /// - Returns: bind result
+ @discardableResult
+ public func bindData(_ data:DataSymbolSupportedDataType) -> Bool {
+ // check data type
+ guard data is Tensor else {
+ SerranoLogging.errorLogging(message: "Tensor symbol (\(self.symbolLabel)) expects a tensor object to bind. Given \(type(of: data)).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return false
+ }
+
+ // check shape
+ let tensor = data as! Tensor
+ guard tensor.shape .== self.shape else {
+ SerranoLogging.errorLogging(message: "Tensor symbol (\(self.symbolLabel)) expects a tensor object with shape \(self.shape.description). Given \(tensor.shape.description)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return false
+ }
+ self._bindedData = tensor
+
+ return true
+ }
+}
diff --git a/Source/Serrano/io/flatbuffer.swift b/Source/Serrano/io/flatbuffer.swift
new file mode 100644
index 0000000..df72e88
--- /dev/null
+++ b/Source/Serrano/io/flatbuffer.swift
@@ -0,0 +1,14 @@
+//
+// flatbuffer.swift
+// Serrano
+//
+// Created by ZHONGHAO LIU on 11/2/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import LibFBSUtil
+
+public func libImport() {
+ LibFBSUtil.hello()
+}
diff --git a/Source/Serrano/model/base_model.swift b/Source/Serrano/model/base_model.swift
new file mode 100644
index 0000000..1284f18
--- /dev/null
+++ b/Source/Serrano/model/base_model.swift
@@ -0,0 +1,12 @@
+//
+// base_model.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 9/19/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+
+
+/// Base mode has train, predict, this kind of function, and work like keras function api
diff --git a/Source/Serrano/model/sequential_model.swift b/Source/Serrano/model/sequential_model.swift
new file mode 100644
index 0000000..0d1c6cb
--- /dev/null
+++ b/Source/Serrano/model/sequential_model.swift
@@ -0,0 +1,12 @@
+//
+// sequential_model.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 9/19/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+
+
+//// only construct sequential model. work like keras sequential model
diff --git a/Source/Serrano/operators/basic/binary/binary_op.metal b/Source/Serrano/operators/basic/binary/binary_op.metal
new file mode 100644
index 0000000..6f0564f
--- /dev/null
+++ b/Source/Serrano/operators/basic/binary/binary_op.metal
@@ -0,0 +1,72 @@
+//
+// binary_op.metal
+// serrano
+//
+// Created by ZHONGHAO LIU on 6/7/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+#include
+using namespace metal;
+
+namespace serrano_ios {
+ kernel void Add(device float* inputA [[ buffer(0) ]],
+ device float* inputB [[ buffer(1) ]],
+ device float* outputC [[ buffer(2) ]],
+ constant uint* count [[ buffer(3) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ outputC[gid.x] = inputA[gid.x] + inputB[gid.x];
+ }
+
+ kernel void Sub(device float* inputA [[ buffer(0) ]],
+ device float* inputB [[ buffer(1) ]],
+ device float* outputC [[ buffer(2) ]],
+ constant uint* count [[ buffer(3) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ outputC[gid.x] = inputA[gid.x] - inputB[gid.x];
+ }
+
+ kernel void Mult(device float* inputA [[ buffer(0) ]],
+ device float* inputB [[ buffer(1) ]],
+ device float* outputC [[ buffer(2) ]],
+ constant uint* count [[ buffer(3) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ outputC[gid.x] = inputA[gid.x] * inputB[gid.x];
+ }
+
+ kernel void Div(device float* inputA [[ buffer(0) ]],
+ device float* inputB [[ buffer(1) ]],
+ device float* outputC [[ buffer(2) ]],
+ constant uint* count [[ buffer(3) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ outputC[gid.x] = inputA[gid.x] / inputB[gid.x];
+ }
+
+ kernel void RDiv(device float* inputA [[ buffer(0) ]],
+ device float* inputB [[ buffer(1) ]],
+ device float* outputC [[ buffer(2) ]],
+ constant uint* count [[ buffer(3) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ outputC[gid.x] = inputB[gid.x] / inputA[gid.x];
+ }
+
+ kernel void Pow(device float* inputA [[ buffer(0) ]],
+ device float* inputB [[ buffer(1) ]],
+ device float* outputC [[ buffer(2) ]],
+ constant uint* count [[ buffer(3) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ outputC[gid.x] = pow(inputA[gid.x], inputB[gid.x]);
+ }
+}
diff --git a/Source/Serrano/operators/basic/binary/binary_op.swift b/Source/Serrano/operators/basic/binary/binary_op.swift
new file mode 100644
index 0000000..7ab7c08
--- /dev/null
+++ b/Source/Serrano/operators/basic/binary/binary_op.swift
@@ -0,0 +1,658 @@
+//
+// binary_op.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import Dispatch
+import Metal
+import Accelerate
+
+/**
+ Abstract class define the standard binary operator working flow.
+ This class should not be used directly.
+ Any class inheritance this class is doing computation on exactly __two__ input tensors in element-wise way and return __one__ result tensor, i.e.
+ `x + y ->>> z`
+ This `BinaryOperator` does __not__ support broadcasting
+ */
+public class BinaryOperator: ComputableOperator {
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - attributes
+
+ /// Operator label. Conforms to `ComputableOperator`
+ public var operatorLabel: String = ""
+
+ /// This operator does not operator on GPU. Conforms to `ComputableOperator`
+ public var metalKernelFuncLabel:String
+
+ /// Conforms to `ComputableOperator`
+ public var computationDelegate: OperatorCalculationDelegate?
+
+ /// Conforms to `ComputableOperator`
+ public var inputTensors: [Tensor]?
+
+ /// Conforms to `ComputableOperator`
+ public var outputTensors: [Tensor]?
+
+ /// The element compuation block in CPU mode.
+ /// In most cases, subclass should just override this part in `init` method instead overriding the whole `cpu(inputTensors:[Tensor], resultTensor: Tensor)` method.
+ /// The fisr tensor is input tensor A;
+ /// the seconds tensor is input tensor B;
+ /// the third tensor is output tensor C.
+ /// This block should do some computation and assign value back to result tensor's reader
+ public var cpuElementComputationBlock: (Tensor, Tensor, Tensor) -> Void
+
+
+ /// The grad compuation block.
+ /// parameter: inputA, inputB, mode
+ /// returns: An array of tensor. Should just have 2 object corresponding to two inputs
+ public var gradComputationBlock: (Tensor, Tensor, OperatorComputationMode) -> [Tensor]
+
+ /// If `true`, operator will not check the `upGrads`'s shape.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ /// Cases like auto generated differentiation graph.
+ public var disableUpGradShapeCheck: Bool = false
+
+ /// If `true`, operator will not call `inputOutputTensorsCheck()` before doing calculation.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ public var disableInputOutputCheck: Bool = false
+
+ /// Indicate if this operator would do paramter update.
+ ///
+ /// - Note: All `UnaryOperators` are not trainable.
+ public var trainable: Bool = false
+
+ /// The mapping type of this operator.
+ /// `Constant` for this operator.
+ public var mapType: OperatorMappingType {
+ get {
+ return OperatorMappingType.Constant
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+ /// Designated init function
+ ///
+ /// - Parameters:
+ /// - label: label description
+ /// - delegate: delegate description
+ public init(operatorLabel label: String,
+ cpuComputeBlock block: @escaping (Tensor, Tensor, Tensor) -> Void ,
+ gradComputationBlock gradBlock: @escaping (Tensor, Tensor, OperatorComputationMode) -> [Tensor],
+ metalKernelFuncLabel kernelLabel: String,
+ computationDelegate: OperatorCalculationDelegate?) {
+ self.operatorLabel = label
+ self.computationDelegate = computationDelegate
+ self.metalKernelFuncLabel = kernelLabel
+ self.cpuElementComputationBlock = block
+ self.gradComputationBlock = gradBlock
+ }
+
+ /// Convenience initializer
+ /// Subclass required to override this function to assign `cpuComputeBlock` and `metalKernelFuncLabel`
+ ///
+ required public convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (inputA: Tensor, inputB: Tensor, outputC: Tensor) -> Void in
+ fatalError("NEED OVERRIDE")
+ }
+ let gradBlock = { (inputA: Tensor, inputB: Tensor, mode: OperatorComputationMode) -> [Tensor] in
+ fatalError("NEED OVERRIDE")
+ }
+ let defaultLabel = "NEED OVERRIDE"
+ let kernelLabel = "NEED OVERRIDE"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate)
+ }
+
+
+ /// Convenience initializer
+ ///
+ /// - Parameters:
+ /// - computationDelegate: computationDelegate description
+ /// - inputTensors: inputTensors description
+ /// - outputTensors: outputTensors description
+ public convenience init(computationDelegate: OperatorCalculationDelegate? = nil,
+ inputTensors: [Tensor], outputTensors: [Tensor]) {
+ self.init(computationDelegate: computationDelegate)
+ self.inputTensors = inputTensors
+ self.outputTensors = outputTensors
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+ /// This operator should just receive two tensors with same dimensions (dataType could be different).
+ /// Return shape is exactly the same shape as input.
+ ///
+ /// - Parameter shapes: input shapes
+ /// - Returns: return shapes
+ public func outputShape(shapeArray shapes: [TensorShape]) -> [TensorShape]? {
+ guard shapes.count == 2 else {
+ SerranoLogging.errorLogging(message: "Operator \(self.operatorLabel) could just receive two input tensors. Given \(shapes.count)", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ // dimension size should be the same. Ignore type
+ guard shapes[0] == shapes[1] else {
+ SerranoLogging.errorLogging(message: "Operator \(self.operatorLabel) receive two shapes not dimension equal. Given \(shapes[0].shapeArray) and \(shapes[1].shapeArray)", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ return [shapes[0]]
+ }
+
+ /// The `inputTensors` should have exactly two tensors and same dimensions.
+ /// The `outputTensors` should have exactly one tensora and same dimension with input tensors.
+ ///
+ ///
+ public func inputOutputTensorsCheck() -> (check: Bool, msg: String) {
+ // inpu tensor and output tensor not nil
+ guard self.inputTensors != nil && self.outputTensors != nil else {
+ return (false, "Operator \(self.operatorLabel) should non-nil inputTensors and outputTensors.")
+ }
+
+ // input tensor shapes checck
+ let inputShapes = self.inputTensors!.map { $0.shape }
+ guard self.outputShape(shapeArray: inputShapes) != nil else {
+ return (false, "Operator \(self.operatorLabel) does not have valid input tensors.")
+ }
+
+ // output tensors check
+ guard self.outputTensors!.count == 1 else {
+ return (false, "Operator \(self.operatorLabel) does not have valid number of output tensors. Require 1, given \(self.outputTensors!.count)")
+ }
+
+ // input tensor and output tensor shape match checking
+ guard self.outputTensors![0].shape == inputShapes[0] else {
+ return (false, "Operator \(self.operatorLabel) does not have valid output tensor shapes. Require \(inputShapes[0]), given \(self.outputTensors![0].count)")
+ }
+
+ return (true, "")
+ }
+
+ /// Compute asynclly
+ ///
+ /// - Parameters:
+ /// - tensors: input tensors
+ /// - computationMode: computation mode
+ public func computeAsync(_ computationMode: OperatorComputationMode = SerranoEngine.configuredEngine.defaultComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.compute(computationMode)
+ }
+ }
+
+ /// Compute synclly.
+ /// - Parameters:
+ /// - tensors: input tensors
+ /// - computationMode: cmputation mode. If choose `GPU` but haven't configued a GPU SerranoEngine, operator will use `CPU` to compute.
+ /// - Returns: result tensors
+ public func compute(_ computationMode: OperatorComputationMode = SerranoEngine.configuredEngine.defaultComputationMode) {
+ // check
+ let (pass, msg) = self.inputOutputTensorsCheck()
+ guard pass else {
+ SerranoLogging.errorLogging(message: "Operator \(self.operatorLabel) calculation aborted cause invalid input tensors or output tensors: \(msg)", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+
+ self.computationDelegate?.operatorWillBeginComputation(self)
+
+ switch computationMode {
+ case .GPU:
+ if !SerranoEngine.configuredEngine.hasAvailableGPU() {
+ SerranoLogging.warningLogging(message: "Serrano Engine has no available configured GPU device. Use CPU doing calculation instead.", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ self.cpu()
+ } else {
+ self.gpu()
+ }
+ case .CPU:
+ self.cpu()
+ case .Auto:
+ // TODO: More intelligent way to decide
+ if self.inputTensors![0].count > 1000000 && SerranoEngine.configuredEngine.hasAvailableGPU(){
+ self.gpu()
+ } else {
+ self.cpu()
+ }
+ }
+
+ self.computationDelegate?.operatorDidEndComputation(self, outputTensors: self.outputTensors!)
+
+ }
+
+ /// Calulate grads sync.
+ /// All unary operator return grads tensor with same number and shape as attribute `inputTensors`.
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ /// - upGrds: upGrds
+ /// - Returns: return grads tensor
+ public func gradCompute(_ computationMode: OperatorComputationMode) -> [String: DataSymbolSupportedDataType] {
+ let grads = self.gradComputationBlock(self.inputTensors![0], self.inputTensors![1], computationMode)
+ var result = [String: DataSymbolSupportedDataType]()
+ for (i, grad) in grads.enumerated() {
+ result["input_\(i)"] = grad
+ }
+ return result
+ }
+
+ /// Cal grads async
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ /// - upGrds: upGrds
+ public func gradComputAsync(_ computationMode: OperatorComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.computationDelegate?.operatorWillBeginGradsComputation(self)
+ let result = self.gradCompute(computationMode)
+ self.computationDelegate?.operatorDidEndGradsComputation(self, grads: result)
+ }
+ }
+
+ /// Update params if possible.
+ /// No update parameters for binary operators.
+ ///
+ /// - Parameters:
+ /// - grads: grads tensor list
+ /// - LR: learning rate
+ public func updateParams(grads: [Tensor], LR: Float) {
+ return
+ }
+
+ /// Binary operator has no parameters. Do nothing
+ public func bindParamSymbols(_ symbols: [GraphSymbol]) {
+ }
+
+ /// This operator has no parameters.
+ ///
+ /// - Returns: An empty array
+ public func paramSymbols() -> [GraphSymbol] {
+ return [GraphSymbol]()
+ }
+
+ /// Use cpu do the inplace computation. This function always do inPlace computation for inputTensorA.
+ /// It's caller function to decide the tensor's assignment.
+ /// Default, `UnaryOperator` defines a workflow. Subclass just needs to override `cpuElementComputationBlock`.
+ /// If subclass needs custom flow, it could just override this function.
+ ///
+ /// - Note: This function should not be called from outside.
+ ///
+ /// - Parameter tensors: the operation tensors
+ internal func cpu() {
+ self.cpuElementComputationBlock(self.inputTensors![0], self.inputTensors![1], self.outputTensors![0])
+ }
+
+ /// Let GPU call the Metal kernel to do the inplace computation.This function always do inPlace computation for inputTensorA.
+ /// It's caller function to decide the tensor's assignment.
+ /// Default, `UnaryOperator` defines a workflow. Subclass just needs to override `metalKernelFuncLabel` attribute.
+ /// If subclass needs custom flow, it could just override this function.
+ ///
+ /// - Note: This function should not be called from outside.
+ ///
+ /// - Parameter tensors: the operation tensors
+ internal func gpu() {
+ // prepare resources
+ let resourcePrepareGroup = DispatchGroup()
+ let engine = SerranoEngine.configuredEngine
+ var kernel: MTLComputePipelineState?
+ var commandBuffer: MTLCommandBuffer?
+ var dataBuffers: [MTLBufferResource] = [MTLBufferResource]()
+ var countBuffer: MTLBuffer?
+
+ // kernel
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ var info = ""
+ (kernel, info) = engine.loadGPUKernel(kernelLabel: self.metalKernelFuncLabel)
+ guard kernel != nil else {
+ fatalError("[Serrano] Failed to load kernel \(self.metalKernelFuncLabel). Info: \(info)")
+ }
+ resourcePrepareGroup.leave()
+ }
+
+ // command buffer
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ commandBuffer = engine.serranoCommandQueue?.makeCommandBuffer()
+ guard commandBuffer != nil else {
+ fatalError("[Serrano] Failed to make new command buffer.")
+ }
+ resourcePrepareGroup.leave()
+ }
+
+ //// Prepare MTLBuffers
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ dataBuffers = SerranoResourceManager.globalManager.allocateMTLBufferResources([self.inputTensors![0], self.inputTensors![1], self.outputTensors![0]])
+
+ resourcePrepareGroup.leave()
+ }
+
+ resourcePrepareGroup.wait()
+
+
+ // dimensionBuffer
+ var count = UInt32(self.inputTensors![0].count)
+ countBuffer = engine.GPUDevice?.makeBuffer(bytes: &count, length: MemoryLayout.size)
+ guard countBuffer != nil else { fatalError("[Serrano] Failed to careate MTLBuffer.") }
+ SerranoLogging.stdLogging(message: "Allocated a Metal buffer [\(countBuffer!.length) bytes] requested for count info \(count) by operator \(self.operatorLabel)", file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+
+ //// Prepare encoders.
+ let encoder = commandBuffer!.makeComputeCommandEncoder()
+ encoder.setComputePipelineState(kernel!)
+ encoder.setBuffer(dataBuffers[0].buffer, offset: dataBuffers[0].offset, at: 0)
+ encoder.setBuffer(dataBuffers[1].buffer, offset: dataBuffers[1].offset, at: 1)
+ encoder.setBuffer(dataBuffers[2].buffer, offset: dataBuffers[2].offset, at: 2)
+ encoder.setBuffer(countBuffer, offset: 0, at: 3)
+
+ // dispatch
+ let threadsPerThreadgroup = MTLSizeMake(kernel!.threadExecutionWidth,
+ 1,
+ 1)
+ let threadgroupsPerGrid = MTLSizeMake((self.inputTensors![0].count + threadsPerThreadgroup.width - 1) / threadsPerThreadgroup.width,
+ 1,
+ 1)
+ encoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup)
+ SerranoLogging.stdLogging(message: "Dispatch group configured with threadgroupsPerGrid: \(threadgroupsPerGrid), threadsPerThreadgroup: \(threadsPerThreadgroup) requested by operator \(self.operatorLabel)", file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+
+ encoder.endEncoding()
+
+ // commit command buffer
+ commandBuffer!.commit()
+ commandBuffer!.waitUntilCompleted()
+ }
+}
+
+/**
+ Do `a+b`. Not support broadcasting.
+ */
+public class AddOperator: BinaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (inputA: Tensor, inputB: Tensor, outputC: Tensor) -> Void in
+ let inputAAddress = inputA.contentsAddress
+ let inputBAddress = inputB.contentsAddress
+ let outputCAddress = outputC.contentsAddress
+ let count = vDSP_Length(outputC.count)
+ vDSP_vadd(inputAAddress, 1, inputBAddress, 1, outputCAddress, 1, count)
+ }
+
+ // dc/da = 1; dc/db = 1
+ let gradBlock = { (inputA: Tensor, inputB: Tensor, mode: OperatorComputationMode) -> [Tensor] in
+ let gradA = Tensor(repeatingValue: 1.0, tensorShape: inputA.shape)
+ let gradB = Tensor(repeatingValue: 1.0, tensorShape: inputB.shape)
+ return [gradA, gradB]
+ }
+
+
+ let defaultLabel = "AddOperator"
+ let kernelLabel = "Add"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate)
+ }
+}
+
+/**
+Do `a-b`. Not support broadcasting.
+*/
+public class SubOperator: BinaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (inputA: Tensor, inputB: Tensor, outputC: Tensor) -> Void in
+ let inputAAddress = inputA.contentsAddress
+ let inputBAddress = inputB.contentsAddress
+ let outputCAddress = outputC.contentsAddress
+ let count = vDSP_Length(outputC.count)
+ vDSP_vsub(inputBAddress, 1, inputAAddress, 1, outputCAddress, 1, count)
+ }
+
+ // dc/da = 1; dc/db = -1
+ let gradBlock = { (inputA: Tensor, inputB: Tensor, mode: OperatorComputationMode) -> [Tensor] in
+ let gradA = Tensor(repeatingValue: 1.0, tensorShape: inputA.shape)
+ let gradB = Tensor(repeatingValue: -1.0, tensorShape: inputB.shape)
+ return [gradA, gradB]
+ }
+
+ let defaultLabel = "SubOperator"
+ let kernelLabel = "Sub"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate)
+ }
+}
+
+/**
+Do `a*b` in element-wise way. Not support broadcasting.
+*/
+public class MultOperator: BinaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (inputA: Tensor, inputB: Tensor, outputC: Tensor) -> Void in
+ let inputAAddress = inputA.contentsAddress
+ let inputBAddress = inputB.contentsAddress
+ let outputCAddress = outputC.contentsAddress
+ let count = vDSP_Length(outputC.count)
+ vDSP_vmul(inputAAddress, 1, inputBAddress, 1, outputCAddress, 1, count)
+ }
+
+ // dc/da = b; dc/db = a
+ let gradBlock = { (inputA: Tensor, inputB: Tensor, mode: OperatorComputationMode) -> [Tensor] in
+ /// First allocate as managed to speed up incase using GPU with reusing MTLBuffers
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors([inputA.shape, inputB.shape])
+ let copyOp = CopyOperator(inputTensors: [inputA, inputB], outputTensors: [grads[1], grads[0]])
+ copyOp.disableInputOutputCheck = true
+ copyOp.compute(mode)
+ return grads
+ }
+
+ let defaultLabel = "MultOperator"
+ let kernelLabel = "Mult"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate)
+ }
+}
+
+/**
+Do `a/b` in element-wise way. Not support broadcasting.
+*/
+public class DivOperator: BinaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (inputA: Tensor, inputB: Tensor, outputC: Tensor) -> Void in
+ let inputAAddress = inputA.contentsAddress
+ let inputBAddress = inputB.contentsAddress
+ let outputCAddress = outputC.contentsAddress
+ let count = vDSP_Length(outputC.count)
+ vDSP_vdiv(inputBAddress, 1, inputAAddress, 1, outputCAddress, 1, count)
+ }
+
+ // dc/da = 1/b; dc/db = -a/b^2
+ let gradBlock = { (inputA: Tensor, inputB: Tensor, mode: OperatorComputationMode) -> [Tensor] in
+ let workGroup = DispatchGroup()
+
+ // A
+ var gradA: Tensor?
+ workGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ gradA = 1.0 / inputB
+ workGroup.leave()
+ }
+
+ // B
+ var gradB: Tensor?
+ workGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ // a / b^2
+ gradB = inputB * inputB
+ let divOp = DivOperator(inputTensors: [inputA, gradB!], outputTensors: [gradB!])
+ divOp.disableInputOutputCheck = true
+ divOp.compute(mode)
+ // -1.0
+ -1.0 &* gradB!
+ workGroup.leave()
+ }
+ workGroup.wait()
+ return [gradA!, gradB!]
+ }
+
+ let defaultLabel = "DivOperator"
+ let kernelLabel = "Div"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate)
+ }
+}
+
+
+/**
+Do `b/a` in element-wise way. Not support broadcasting.
+*/
+public class RDivOperator: BinaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (inputA: Tensor, inputB: Tensor, outputC: Tensor) -> Void in
+ let inputAAddress = inputA.contentsAddress
+ let inputBAddress = inputB.contentsAddress
+ let outputCAddress = outputC.contentsAddress
+ let count = vDSP_Length(outputC.count)
+ vDSP_vdiv(inputAAddress, 1, inputBAddress, 1, outputCAddress, 1, count)
+ }
+
+ // dc/da = -b/a^2; dc/db = 1/a
+ let gradBlock = { (inputA: Tensor, inputB: Tensor, mode: OperatorComputationMode) -> [Tensor] in
+ let workGroup = DispatchGroup()
+
+ // A
+ var gradA: Tensor?
+ workGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ // b / a^2
+ gradA = inputA * inputA
+ let divOp = DivOperator(inputTensors: [inputB, gradA!], outputTensors: [gradA!])
+ divOp.disableInputOutputCheck = true
+ divOp.compute(mode)
+ // -1.0
+ -1.0 &* gradA!
+ workGroup.leave()
+ }
+
+ // B
+ var gradB: Tensor?
+ workGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ gradB = 1.0 / inputA
+ workGroup.leave()
+ }
+
+ workGroup.wait()
+ return [gradA!, gradB!]
+ }
+
+
+ let defaultLabel = "RDivOperator"
+ let kernelLabel = "RDiv"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate)
+ }
+}
+
+/**
+Do `a^b` in element-wise way. Not support broadcasting.
+
+- Note: This operator may output `NaN` or `Infinite` values and operator would not check these situations.
+*/
+public class PowOperator: BinaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (inputA: Tensor, inputB: Tensor, outputC: Tensor) -> Void in
+ let inputAAddress = inputA.contentsAddress
+ let inputBAddress = inputB.contentsAddress
+ let outputCAddress = outputC.contentsAddress
+ var count = Int32(outputC.count)
+ vvpowf(outputCAddress, inputBAddress, inputAAddress, &count)
+ }
+
+ // dc/da = b * a^(b-1); dc/db = (a^b) * ln(a)
+ let gradBlock = { (inputA: Tensor, inputB: Tensor, mode: OperatorComputationMode) -> [Tensor] in
+ let workGroup = DispatchGroup()
+
+ // A
+ var gradA: Tensor?
+ workGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ gradA = SerranoResourceManager.globalManager.allocateUnamangedTensor(inputA.shape)
+ let bm1 = inputB - 1.0
+ // a^(b-1)
+ let powOp = PowOperator(inputTensors: [inputA, bm1], outputTensors: [gradA!])
+ powOp.disableInputOutputCheck = true
+ powOp.compute(mode)
+ // * b
+ gradA! &* inputB
+ workGroup.leave()
+ }
+
+ // B
+ var gradB: Tensor?
+ workGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ gradB = SerranoResourceManager.globalManager.allocateUnamangedTensor(inputB.shape)
+ // lna
+ let group = DispatchGroup()
+ let lna = Tensor(repeatingValue: 0.0, tensorShape: inputA.shape)
+ DispatchQueue.global(qos: .userInitiated).async {
+ group.enter()
+ let logOp = LogOperator(inputTensors: [inputA], outputTensors: [lna])
+ logOp.disableInputOutputCheck = true
+ logOp.compute(mode)
+ group.leave()
+ }
+
+ // (a^b)
+ let powOp = PowOperator(inputTensors: [inputA, inputB], outputTensors: [gradB!])
+ powOp.disableInputOutputCheck = true
+ powOp.compute(mode)
+
+ // *
+ group.wait()
+ gradB! &* lna
+
+ workGroup.leave()
+ }
+
+ workGroup.wait()
+ return [gradA!, gradB!]
+ }
+
+ let defaultLabel = "PowOperator"
+ let kernelLabel = "Pow"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate)
+ }
+}
+
diff --git a/Source/Serrano/operators/basic/broadcast/broadcast_arithmetic_ops.swift b/Source/Serrano/operators/basic/broadcast/broadcast_arithmetic_ops.swift
new file mode 100644
index 0000000..780fa9e
--- /dev/null
+++ b/Source/Serrano/operators/basic/broadcast/broadcast_arithmetic_ops.swift
@@ -0,0 +1,424 @@
+//
+// tensor_arithmetic_ops.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/4/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+
+
+/**
+The abstract parent class for all broadcast arithmetic oeprators.
+Any child class of this operator support element-wise calculation between two `Tensor` objects with broadcasting support.
+*/
+public class BroadcastArithmeticOperator: ComputableOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+ public var computationDelegate: OperatorCalculationDelegate?
+
+ public var metalKernelFuncLabel: String
+
+ public var operatorLabel: String
+
+ public var inputTensors: [Tensor]?
+
+ public var outputTensors: [Tensor]?
+
+ /// Computation logic block.
+ /// First param is input tensors, seconds param is output tensors.
+ /// Operator just needs override this in requried init function instead of override whole computation methods.
+ /// All input tensors are already broadcasted.
+ public lazy var calculationBlock: ([Tensor], [Tensor], OperatorComputationMode) -> Void = {_,_,_ in }
+
+ /// The grad compuation block.
+ /// parameter: inputA, inputB,
+ /// returns: An array of tensor. Should just have 2 object corresponding to two inputs
+ public var gradComputationBlock: (Tensor, Tensor, OperatorComputationMode) -> [Tensor] = {(_,_,_) -> [Tensor] in
+ fatalError("Not implemented")
+ }
+
+
+ /// If `true`, operator will not check the `upGrads`'s shape.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ /// Cases like auto generated differentiation graph.
+ public var disableUpGradShapeCheck: Bool = false
+
+ /// If `true`, operator will not call `inputOutputTensorsCheck()` before doing calculation.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ public var disableInputOutputCheck: Bool = false
+
+ /// Indicate if this operator would do paramter update.
+ ///
+ /// - Note: `BroadcastArithmeticOperator` is not trainable.
+ public var trainable: Bool = false
+
+ /// The mapping type of this operator.
+ /// `OneToOne` for this operator.
+ public var mapType: OperatorMappingType {
+ get {
+ return OperatorMappingType.OneToOne
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+
+ /// Initializer.
+ ///
+ /// - Note: In most cases this initializer should not be called directly.
+ /// call one of the convenience initializers instead.
+ ///
+ /// - Parameters:
+ /// - computationDelegate: computationDelegate
+ /// - inputTensors: inputTensors
+ /// - outputTensors: outputTensors
+ /// - metalKernelFuncLabel: metalKernelFuncLabel
+ /// - operatorLabel: operatorLabel
+ public init(computationDelegate: OperatorCalculationDelegate?,
+ inputTensors: [Tensor]?, outputTensors: [Tensor]?,
+ metalKernelFuncLabel: String,
+ operatorLabel: String) {
+ self.computationDelegate = computationDelegate
+ self.inputTensors = inputTensors
+ self.outputTensors = outputTensors
+ self.metalKernelFuncLabel = metalKernelFuncLabel
+ self.operatorLabel = operatorLabel
+ }
+
+ /// Conevnience initializer.
+ ///
+ /// - Note: All subclass should override this convenience initializer to setup `metalKernelFuncLabel` and default `operatorLabel`.
+ ///
+ /// - Parameter computationDelegate: computationDelegate
+ required public convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ // no need metal. Just use other existing operators
+ let metalKernelFuncLabel = ""
+ let defaultLable = "OVERRIDE"
+ self.init(computationDelegate: computationDelegate, inputTensors: nil, outputTensors: nil,
+ metalKernelFuncLabel: metalKernelFuncLabel, operatorLabel: defaultLable)
+ }
+
+ /// Conenience initializer.
+ ///
+ /// - Parameters:
+ /// - computationDelegate: computationDelegate
+ /// - inputTensors: inputTensors
+ /// - outputTensors: outputTensors
+ public convenience init(computationDelegate: OperatorCalculationDelegate? = nil, inputTensors: [Tensor], outputTensors: [Tensor]) {
+ self.init(computationDelegate: computationDelegate)
+ self.inputTensors = inputTensors
+ self.outputTensors = outputTensors
+ }
+
+
+ /// Convenience initializer
+ ///
+ /// - Parameters:
+ /// - computationDelegate: computationDelegate
+ /// - operatorLabel: operatorLabel
+ public convenience init(computationDelegate: OperatorCalculationDelegate? = nil, operatorLabel: String) {
+ self.init(computationDelegate: computationDelegate)
+ self.operatorLabel = operatorLabel
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+
+ /// Check the input shapes.
+ /// Should exactly contains two shapes and the two shapes should be have same dimensions with or without broadcasting.
+ ///
+ /// - Parameter shapes: input shapes
+ /// - Returns: output shapes, maybe `nil` if not valid
+ public func outputShape(shapeArray shapes: [TensorShape]) -> [TensorShape]? {
+ // two input shapes
+ guard shapes.count == 2 else {
+ SerranoLogging.errorLogging(message: "Input shapes should have exactly 2 shapes. Given \(shapes.count)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ // shape match
+ let shapeA = shapes[0]
+ let shapeB = shapes[1]
+ if shapeA == shapeB {
+ // same dim
+ return [shapeA]
+ } else {
+ // if not same dims, check if could broadcast to higher rank shape
+ let broadcastOp = BroadcastOperator(targetShape: max(shapeA, shapeB))
+ if shapeA < shapeB {
+ return broadcastOp.outputShape(shapeArray: [shapeA])
+ } else {
+ return broadcastOp.outputShape(shapeArray: [shapeB])
+ }
+ }
+ }
+
+
+ /// Check validation of shapes mathcing between `inpuTensors` and `outputTensors`.
+ ///
+ /// - Returns: `check` indicates if validation, `msg` containing error information.
+ public func inputOutputTensorsCheck() -> (check: Bool, msg: String) {
+ // input not nil
+ guard self.inputTensors != nil else {
+ return (false, "Input tensors are nil")
+ }
+
+ // output not nil
+ guard self.outputTensors != nil else {
+ return (false, "Output tensors are nil")
+ }
+
+ // check input shapes
+ let inputShapes = self.inputTensors!.map { $0.shape }
+ let outputShapeCheck = self.outputShape(shapeArray: inputShapes)
+ guard outputShapeCheck != nil else {
+ return (false, "Invalid shapes from input tensors. Check log for details.")
+ }
+
+ // check output shape match
+ let outputShapes = self.outputTensors!.map { $0.shape }
+ guard outputShapeCheck!.count == outputShapes.count else {
+ return (false, "Ouput tensors amount is not valid. Expect \(outputShapeCheck!.count), given \(outputShapes.count).")
+ }
+ for (shape, shapeCheck) in zip(outputShapes, outputShapeCheck!) {
+ guard shape == shapeCheck else {
+ return (false, "Invalid shape in output tensors. Expect \(shapeCheck.description), given \(shape.description)")
+ }
+ }
+
+ return (true, "")
+ }
+
+
+ /// Usually, a `BroadcastArithmeticOperator` just call `BroadcastOperator` and other calculation operators.
+ /// This methods will first do braodcast on input tensors if needs and then call `calculationBlock`.
+ /// - Parameter computationMode: computationMode
+ public func compute(_ computationMode: OperatorComputationMode = SerranoEngine.configuredEngine.defaultComputationMode) {
+ // check
+ let (pass, msg) = self.inputOutputTensorsCheck()
+ guard pass else {
+ SerranoLogging.errorLogging(message: msg, file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+
+ self.computationDelegate?.operatorWillBeginComputation(self)
+
+ var inputA: Tensor = self.self.inputTensors![0]
+ var inputB: Tensor = self.self.inputTensors![1]
+
+ // Do broadcast if needs
+ let shapeA = self.inputTensors![0].shape
+ let shapeB = self.inputTensors![1].shape
+ if shapeA != shapeB {
+ let broadcastOp = BroadcastOperator(targetShape: max(shapeA, shapeB))
+ if shapeA < shapeB {
+ // broadcast A
+ broadcastOp.inputTensors = [self.inputTensors![0]]
+ inputA = SerranoResourceManager.globalManager.allocateTensor(shapeB)
+ broadcastOp.outputTensors = [inputA]
+ } else if shapeA > shapeB {
+ // broadcast B
+ broadcastOp.inputTensors = [self.inputTensors![1]]
+ inputB = SerranoResourceManager.globalManager.allocateTensor(shapeA)
+ broadcastOp.outputTensors = [inputB]
+ }
+ broadcastOp.compute(computationMode)
+ }
+
+ // call calculation block
+ self.calculationBlock([inputA, inputB], self.outputTensors!, computationMode)
+
+ // return intermediate tensors
+ if inputA != self.inputTensors![0] { SerranoResourceManager.globalManager.returnTensor(inputA) }
+ if inputB != self.inputTensors![1] { SerranoResourceManager.globalManager.returnTensor(inputB) }
+
+ self.computationDelegate?.operatorDidEndComputation(self, outputTensors: self.outputTensors!)
+ }
+
+ public func computeAsync(_ computationMode: OperatorComputationMode = SerranoEngine.configuredEngine.defaultComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.compute(computationMode)
+ }
+ }
+
+ /// Calulate grads sync.
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ /// - Returns: return grads tensor
+ public func gradCompute(_ computationMode: OperatorComputationMode) -> [String: DataSymbolSupportedDataType] {
+ let grads = self.gradComputationBlock(self.inputTensors![0], self.inputTensors![1], computationMode)
+ var result = [String: DataSymbolSupportedDataType]()
+ for (i, grad) in grads.enumerated() {
+ result["input_\(i)"] = grad
+ }
+ return result
+ }
+
+ /// Cal grads async
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ /// - upGrds: upGrds
+ public func gradComputAsync(_ computationMode: OperatorComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.computationDelegate?.operatorWillBeginGradsComputation(self)
+ let result = self.gradCompute(computationMode)
+ self.computationDelegate?.operatorDidEndGradsComputation(self, grads: result)
+ }
+ }
+
+ /// Update params if possible.
+ /// No update parameters for broadcast arithmetic operators.
+ ///
+ /// - Parameters:
+ /// - grads: grads tensor list
+ /// - LR: learning rate
+ public func updateParams(grads: [Tensor], LR: Float) {
+ return
+ }
+
+ /// This operator has no parameters. Do nothing
+ ///
+ public func bindParamSymbols(_ symbols: [GraphSymbol]) {
+
+ }
+
+ /// This operator has no parameters.
+ ///
+ /// - Returns: An empty array
+ public func paramSymbols() -> [GraphSymbol] {
+ return [GraphSymbol]()
+ }
+}
+
+/**
+Broadcasting addition.
+
+- Note: The `inputTensors` of this operator should just have exactly 2 tensors and the `outputTensors` should just have 1 tensor.
+*/
+public class BroadcastAddOperator: BroadcastArithmeticOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+ /// Override requred inializer
+ ///
+ /// - Parameter computationDelegate: computationDelegate
+ required public convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let metalKernelFuncLabel = ""
+ let defaultLable = "BroadcastAddOp"
+ self.init(computationDelegate: computationDelegate, inputTensors: nil, outputTensors: nil,
+ metalKernelFuncLabel: metalKernelFuncLabel, operatorLabel: defaultLable)
+ self.calculationBlock = { (inputTensors: [Tensor], outputTensors: [Tensor], computationMode: OperatorComputationMode) -> Void in
+ let inputA = inputTensors[0]
+ let inputB = inputTensors[1]
+ let output = outputTensors[0]
+ // element wise add
+ let addOp = AddOperator(inputTensors: [inputA, inputB], outputTensors: [output])
+ addOp.compute(computationMode)
+ }
+
+ //TODO: Implement gradBlock
+ }
+}
+
+/**
+Broadcasting substraction.
+
+- Note: The `inputTensors` of this operator should just have exactly 2 tensors and the `outputTensors` should just have 1 tensor.
+*/
+public class BroadcastSubOperator: BroadcastArithmeticOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+ /// Override requred inializer
+ ///
+ /// - Parameter computationDelegate: computationDelegate
+ required public convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let metalKernelFuncLabel = ""
+ let defaultLable = "BroadcastSubOp"
+ self.init(computationDelegate: computationDelegate, inputTensors: nil, outputTensors: nil,
+ metalKernelFuncLabel: metalKernelFuncLabel, operatorLabel: defaultLable)
+ self.calculationBlock = { (inputTensors: [Tensor], outputTensors: [Tensor], computationMode: OperatorComputationMode) -> Void in
+ let inputA = inputTensors[0]
+ let inputB = inputTensors[1]
+ let output = outputTensors[0]
+ // element wise add
+ let subOp = SubOperator(inputTensors: [inputA, inputB], outputTensors: [output])
+ subOp.compute(computationMode)
+ }
+
+ //TODO: Implement gradBlock
+ }
+}
+
+/**
+Broadcasting multiplication.
+
+- Note: The `inputTensors` of this operator should just have exactly 2 tensors and the `outputTensors` should just have 1 tensor.
+*/
+public class BroadcastMultOperator: BroadcastArithmeticOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+ /// Override requred inializer
+ ///
+ /// - Parameter computationDelegate: computationDelegate
+ required public convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let metalKernelFuncLabel = ""
+ let defaultLable = "BroadcastMultOp"
+ self.init(computationDelegate: computationDelegate, inputTensors: nil, outputTensors: nil,
+ metalKernelFuncLabel: metalKernelFuncLabel, operatorLabel: defaultLable)
+ self.calculationBlock = { (inputTensors: [Tensor], outputTensors: [Tensor], computationMode: OperatorComputationMode) -> Void in
+ let inputA = inputTensors[0]
+ let inputB = inputTensors[1]
+ let output = outputTensors[0]
+ // element wise add
+ let multOp = MultOperator(inputTensors: [inputA, inputB], outputTensors: [output])
+ multOp.compute(computationMode)
+ }
+
+ //TODO: Implement gradBlock
+ }
+}
+
+/**
+Broadcasting division.
+
+- Note: The `inputTensors` of this operator should just have exactly 2 tensors and the `outputTensors` should just have 1 tensor.
+*/
+public class BroadcastDivOperator: BroadcastArithmeticOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+ /// Override requred inializer
+ ///
+ /// - Parameter computationDelegate: computationDelegate
+ required public convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let metalKernelFuncLabel = ""
+ let defaultLable = "BroadcastDivOp"
+ self.init(computationDelegate: computationDelegate, inputTensors: nil, outputTensors: nil,
+ metalKernelFuncLabel: metalKernelFuncLabel, operatorLabel: defaultLable)
+ self.calculationBlock = { (inputTensors: [Tensor], outputTensors: [Tensor], computationMode: OperatorComputationMode) -> Void in
+ let inputA = inputTensors[0]
+ let inputB = inputTensors[1]
+ let output = outputTensors[0]
+ // element wise add
+ let divOp = DivOperator(inputTensors: [inputA, inputB], outputTensors: [output])
+ divOp.compute(computationMode)
+ }
+
+ //TODO: Implement gradBlock
+ }
+}
diff --git a/Source/Serrano/operators/basic/broadcast/broadcast_op.swift b/Source/Serrano/operators/basic/broadcast/broadcast_op.swift
new file mode 100644
index 0000000..fd3cd6d
--- /dev/null
+++ b/Source/Serrano/operators/basic/broadcast/broadcast_op.swift
@@ -0,0 +1,331 @@
+//
+// broadcast_op.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 6/13/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+
+/**
+Doing broadcasting on input tensors and store result in output tensors.
+Serrano follows the broadcasting rule of [Scipy](http://scipy.github.io/old-wiki/pages/EricsBroadcastingDoc).
+*/
+public class BroadcastOperator: ComputableOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// Operator label. Conforms to `ComputableOperator`
+ public var operatorLabel: String = ""
+
+ /// This operator does not operator on GPU. Conforms to `ComputableOperator`
+ public var metalKernelFuncLabel:String
+
+ /// Conforms to `ComputableOperator`
+ public var computationDelegate: OperatorCalculationDelegate?
+
+ /// Conforms to `ComputableOperator`
+ public var inputTensors: [Tensor]?
+
+ /// Conforms to `ComputableOperator`
+ public var outputTensors: [Tensor]?
+
+ /// Target shape.
+ /// - Note: This atrribute could be `nil` when initialize an object.
+ /// If it is `nil` doing calculation, a `fatalError()` will be raise.
+ public var targetShape: TensorShape?
+
+ /// If `true`, operator will not check the `upGrads`'s shape.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ /// Cases like auto generated differentiation graph.
+ public var disableUpGradShapeCheck: Bool = false
+
+ /// If `true`, operator will not call `inputOutputTensorsCheck()` before doing calculation.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ public var disableInputOutputCheck: Bool = false
+
+ /// Indicate if this operator would do paramter update.
+ ///
+ /// - Note: `BroadcastOperator` is not trainable.
+ public var trainable: Bool = false
+
+ /// The mapping type of this operator.
+ /// `OneToOne` for this operator.
+ public var mapType: OperatorMappingType {
+ get {
+ return OperatorMappingType.OneToOne
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+ /// Initializers
+ ///
+ /// - Note: In most cases, this initializer should not be called.
+ /// Call one of the convenience initializers instead.
+ ///
+ /// - Parameters:
+ /// - label:
+ /// - kernelLabel:
+ /// - computationDelegate:
+ /// - inputTensors:
+ /// - outputTensors:
+ /// - targetShape:
+ public init(operatorLabel label: String,
+ metalKernelFuncLabel kernelLabel: String,
+ computationDelegate: OperatorCalculationDelegate?,
+ inputTensors: [Tensor]?,
+ outputTensors: [Tensor]?,
+ targetShape: TensorShape?) {
+ self.operatorLabel = label
+ self.computationDelegate = computationDelegate
+ self.metalKernelFuncLabel = kernelLabel
+ self.inputTensors = inputTensors
+ self.outputTensors = outputTensors
+ self.targetShape = targetShape
+ }
+
+
+ ///
+ ///
+ /// - Parameters:
+ /// - computationDelegate:
+ /// - targetShape:
+ public convenience init(computationDelegate: OperatorCalculationDelegate, targetShape: TensorShape?) {
+ let defaultLabel = "BroadCastOp"
+ let kernelLabel = ""
+ self.init(operatorLabel: defaultLabel, metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil, targetShape: targetShape)
+ }
+
+
+ /// Initializer
+ ///
+ /// - Parameter targetShape: target broadcast shape
+ public convenience init(targetShape: TensorShape? = nil) {
+ let defaultLabel = "BroadCastOp"
+ let kernelLabel = ""
+ self.init(operatorLabel: defaultLabel, metalKernelFuncLabel: kernelLabel, computationDelegate: nil,
+ inputTensors: nil, outputTensors: nil, targetShape: targetShape)
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Conforms to `ComputableOperator`
+
+ /// The input shapes and target shape should follow the [`scipy rule`](http://scipy.github.io/old-wiki/pages/EricsBroadcastingDoc)
+ ///
+ /// - Parameter shapes: input shapes
+ /// - Returns: return shapes. `nil` if not valid
+ public func outputShape(shapeArray shapes: [TensorShape]) -> [TensorShape]? {
+ // target shape set
+ guard self.targetShape != nil else {
+ SerranoLogging.errorLogging(message: "Did not setup targetShape.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ let targetShapeReversed = Array(self.targetShape!.shapeArray.reversed())
+
+ let transformedShape = shapes.flatMap { tensorShape -> TensorShape? in
+ // dimensions check
+ guard tensorShape.shapeArray.count > 0 && tensorShape.shapeArray.count <= self.targetShape!.shapeArray.count else {
+ SerranoLogging.errorLogging(message: "Invalid shape: \(tensorShape.shapeArray) for target shape: \(self.targetShape!.shapeArray)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ // dim size check
+ let tensorShapeReversed = Array(tensorShape.shapeArray.reversed())
+ for (dimInput, dimTarget) in zip(tensorShapeReversed, targetShapeReversed) {
+ guard dimInput == dimTarget || dimInput == 1 else {
+ SerranoLogging.errorLogging(message: "Invalid shape: \(tensorShape.shapeArray) for target shape: \(self.targetShape!.shapeArray)", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+ }
+
+ return self.targetShape
+ }
+
+ if transformedShape.count != shapes.count {
+ return nil
+ } else {
+ return transformedShape
+ }
+ }
+
+
+ /// Check if shapes of all input tensors and output tensors are compatible with `targetShape`
+ ///
+ /// - Returns: check and message
+ public func inputOutputTensorsCheck() -> (check: Bool, msg: String) {
+ guard self.inputTensors != nil else {
+ return (false, "Input tensors are nil")
+ }
+
+ guard self.outputTensors != nil else {
+ return (false, "Output tensors are nil")
+ }
+
+ guard self.inputTensors!.count == self.outputTensors!.count else {
+ return (false, "Input and output tensors should have same number of tensors. Given input: \(self.inputTensors!.count) and output: \(self.outputTensors!.count)")
+ }
+
+ // check each tensor's shape
+ for tensor in self.inputTensors! {
+ guard self.outputShape(shapeArray: [tensor.shape]) != nil else {
+ return (false, "Input tensor \(tensor.description) cannot be broadcast to target shape \(self.targetShape!.description)")
+ }
+ }
+
+ // check each tensor's shape
+ for tensor in self.outputTensors! {
+ guard self.outputShape(shapeArray: [tensor.shape]) != nil else {
+ return (false, "Output tensor \(tensor.description) cannot be broadcast to target shape \(self.targetShape!.description)")
+ }
+ }
+
+ return (true, "")
+ }
+
+ public func compute(_ computationMode: OperatorComputationMode = SerranoEngine.configuredEngine.defaultComputationMode) {
+ // check
+ let (pass, msg) = self.inputOutputTensorsCheck()
+ guard pass else {
+ SerranoLogging.errorLogging(message: "Operator \(self.operatorLabel) calculation aborted cause invalid input tensors or output tensors: \(msg)", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+
+ self.computationDelegate?.operatorWillBeginComputation(self)
+ self.cpu()
+ self.computationDelegate?.operatorDidEndComputation(self, outputTensors: self.outputTensors!)
+ }
+
+ public func computeAsync(_ computationMode: OperatorComputationMode = SerranoEngine.configuredEngine.defaultComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.compute(computationMode)
+ }
+ }
+
+ /// Calulate grads sync.
+ /// Broadcast operator itself does not generate any grads. Should be ignored in gaph AD.
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ /// - Returns: return `upGrads` if not nil. Else return an empty array.
+ public func gradCompute(_ computationMode: OperatorComputationMode) -> [String: DataSymbolSupportedDataType] {
+ return [:]
+ }
+
+
+ /// Cal grads async
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ public func gradComputAsync(_ computationMode: OperatorComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.computationDelegate?.operatorWillBeginGradsComputation(self)
+ let result = self.gradCompute(computationMode)
+ self.computationDelegate?.operatorDidEndGradsComputation(self, grads: result)
+ }
+ }
+
+
+ /// No updatable parameters.
+ /// This function just returns.
+ ///
+ /// - Parameters:
+ /// - grads: grads
+ /// - LR: LR
+ public func updateParams(grads: [Tensor], LR: Float) {
+ return
+ }
+
+ /// This operator has no parameters. Do nothing
+ ///
+ public func bindParamSymbols(_ symbols: [GraphSymbol]) {
+
+ }
+
+ /// This operator has no parameters.
+ ///
+ /// - Returns: An empty array
+ public func paramSymbols() -> [GraphSymbol] {
+ return [GraphSymbol]()
+ }
+
+ public func cpu() {
+ for (tensor, targetTensor) in zip(self.inputTensors!, self.outputTensors!) {
+
+ // get reverse shapes for convenience
+ let targetShapeReversed = Array(self.targetShape!.shapeArray.reversed())
+ var rawShapeReversed = Array(tensor.shape.shapeArray.reversed())
+
+ for i in 0..= rawShapeReversed.count {
+ rawShapeReversed.append(0)
+ }
+ let rawDim = rawShapeReversed[i]
+ let targetDim = targetShapeReversed[i]
+ if i == 0 { // 1st dim
+ if rawDim == targetDim { // just copy each element
+ let cpFromAddress = tensor.contentsAddress
+ let cpToAddress = UnsafeMutableRawPointer(targetTensor.contentsAddress)
+ memcpy(cpToAddress, cpFromAddress, tensor.count * MemoryLayout.stride)
+ } else { // copy each element for targetDim times
+ let reader = tensor.floatValueReader
+ let writer = targetTensor.floatValueReader
+ var writerIndex = 0
+ for elementCount in 0...stride
+
+ // Needs to move first avoid over writing
+ for i in 0...stride
+ let cpFromAddress = UnsafeRawPointer(targetTensor.contentsAddress)
+ var cpToAddress = UnsafeMutableRawPointer(targetTensor.contentsAddress) + blockSize
+ for _ in 1..
+using namespace metal;
+
+namespace serrano_ios {
+
+ typedef struct
+ {
+ uint M; // # of row in transposed matrix
+ uint N; // # of cols in transposed matrix
+ ushort stride; // stride of element
+ } TransposeMatrixInfo;
+
+ kernel void Transpose(const device float* in_tensor [[ buffer(0) ]],
+ const device float* out_tensor [[ buffer(1) ]],
+ constant TransposeMatrixInfo& matrix_info [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]]) {
+ uint M = matrix_info.M;
+ uint N = matrix_info.N;
+ ushort stride = matrix_info.stride;
+
+ if (gid.x >= M || gid.y >= N) return;
+
+ const device float* src = (const device float*)((device char*)in_tensor + (M * gid.y + gid.x) * stride);
+ device float* out = (device float*)((device char*)out_tensor + (N * gid.x + gid.y) * stride);
+ out[0] = src[0];
+ }
+}
diff --git a/Source/Serrano/operators/basic/broadcast/transpose_op.swift b/Source/Serrano/operators/basic/broadcast/transpose_op.swift
new file mode 100644
index 0000000..1bd2022
--- /dev/null
+++ b/Source/Serrano/operators/basic/broadcast/transpose_op.swift
@@ -0,0 +1,343 @@
+//
+// transpose_op.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 6/23/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import Metal
+import Accelerate
+
+public struct TransposeMatrixInfo {
+ var M: MetalUInt
+ var N: MetalUInt
+ var stride: MetalUShort
+}
+
+/**
+Transpose 2D matrix on all `inputTensors` and put transposed values in `outputTensors`.
+*/
+public class TransposeOperator: ComputableOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// Conforms to `ComputableOperator`
+ public var computationDelegate: OperatorCalculationDelegate?
+
+ /// Conforms to `ComputableOperator`
+ public var metalKernelFuncLabel: String = "Transpose"
+
+ /// Conforms to `ComputableOperator`
+ public var operatorLabel: String
+
+ /// Conforms to `ComputableOperator`
+ public var inputTensors: [Tensor]?
+
+ /// Conforms to `ComputableOperator`
+ public var outputTensors: [Tensor]?
+
+ /// If `true`, operator will not check the `upGrads`'s shape.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ /// Cases like auto generated differentiation graph.
+ public var disableUpGradShapeCheck: Bool = false
+
+ /// If `true`, operator will not call `inputOutputTensorsCheck()` before doing calculation.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ public var disableInputOutputCheck: Bool = false
+
+ /// Indicate if this operator would do paramter update.
+ public var trainable: Bool = false
+
+ /// The mapping type of this operator.
+ /// `OneToOne` for this operator.
+ public var mapType: OperatorMappingType {
+ get {
+ return OperatorMappingType.OneToOne
+ }
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+ public init(inputTensors: [Tensor]?, outputTensors: [Tensor]?,
+ computationDelegate: OperatorCalculationDelegate?, operatorLabel: String) {
+ self.inputTensors = inputTensors
+ self.outputTensors = outputTensors
+ self.computationDelegate = computationDelegate
+ self.operatorLabel = operatorLabel
+ }
+
+ public convenience init(computationDelegate: OperatorCalculationDelegate?) {
+ self.init(inputTensors: nil, outputTensors: nil, computationDelegate: computationDelegate, operatorLabel: "TransposeOp")
+ }
+
+ public convenience init(label: String = "TransposeOp") {
+ self.init(inputTensors: nil, outputTensors: nil, computationDelegate: nil, operatorLabel: label)
+ }
+
+ public convenience init(inputTensors: [Tensor], outputTensors: [Tensor]) {
+ self.init(inputTensors: inputTensors, outputTensors: outputTensors, computationDelegate: nil, operatorLabel: "TransposeOp")
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Conforms to `ComputableOperator`
+
+
+ /// Only accepts 2D matrix.
+ ///
+ /// - Parameter shapes: input shapes
+ /// - Returns: output shapes
+ public func outputShape(shapeArray shapes: [TensorShape]) -> [TensorShape]? {
+ guard shapes.count != 0 else {
+ SerranoLogging.errorLogging(message: "Input shapes contains no shape.",
+ file: "\(#file)", function: "#\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ var returnShapes = [TensorShape]()
+ for shape in shapes {
+ guard shape.rank == 2 else {
+ SerranoLogging.errorLogging(message: "Shape \(shape) should has rank values as 2, given \(shape.rank)",
+ file: "\(#file)", function: "#\(#function)", line: "\(#line)")
+ return nil
+ }
+ returnShapes.append(TensorShape(dataType: shape.dataType, shape: [shape.shapeArray[1], shape.shapeArray[0]]))
+ }
+
+ return returnShapes
+ }
+
+
+ /// Check if `inputTensors` and `outputTensors` have correct corresponding tensors.
+ ///
+ /// - Returns: passing and message
+ public func inputOutputTensorsCheck() -> (check: Bool, msg: String) {
+ // input tensors not nil
+ guard self.inputTensors != nil else {
+ return (false, "Input tensors are nil")
+ }
+
+ // output tensors not nil
+ guard self.outputTensors != nil else {
+ return (false, "Output tensors are nil")
+ }
+
+ // same count
+ guard self.inputTensors!.count == self.outputTensors!.count else {
+ return (false, "Input tensors and output tensors have different number of elements. Input tensors: \(self.inputTensors!.count). Output tensors: \(self.outputTensors!.count).")
+ }
+
+ // input shapes check
+ let inputShapes = self.inputTensors!.map { $0.shape }
+ let outputShapesCheck = self.outputShape(shapeArray: inputShapes)
+ guard outputShapesCheck != nil else {
+ return (false, "Input tensors' shapes are not valid. Check log for details.")
+ }
+
+ // comapre shapes
+ let outputShapes = self.outputTensors!.map { $0.shape }
+ for shapeIndex in 0.. 1000000 && SerranoEngine.configuredEngine.hasAvailableGPU(){
+ self.gpu()
+ } else {
+ self.cpu()
+ }
+ }
+ self.computationDelegate?.operatorDidEndComputation(self, outputTensors: self.outputTensors!)
+ }
+
+ public func computeAsync(_ computationMode: OperatorComputationMode = SerranoEngine.configuredEngine.defaultComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.compute(computationMode)
+ }
+ }
+
+ /// Calulate grads sync.
+ /// All unary operator return grads tensor with same number and shape as attribute `inputTensors`.
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ /// - upGrds: upGrds
+ /// - Returns: return grads tensor
+ public func gradCompute(_ computationMode: OperatorComputationMode) -> [String: DataSymbolSupportedDataType] {
+ //TODO: Implementation
+ fatalError("Not implemented")
+ }
+
+ /// Cal grads async
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ /// - upGrds: upGrds
+ public func gradComputAsync(_ computationMode: OperatorComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ _ = self.gradCompute(computationMode)
+ }
+ }
+
+ /// Update params if possible.
+ /// No update parameters for binary operators.
+ ///
+ /// - Parameters:
+ /// - grads: grads tensor list
+ /// - LR: learning rate
+ public func updateParams(grads: [Tensor], LR: Float) {
+ return
+ }
+
+ /// This operator has no parameters. Do nothing
+ ///
+ public func bindParamSymbols(_ symbols: [GraphSymbol]) {
+
+ }
+
+ /// This operator has no parameters.
+ ///
+ /// - Returns: An empty array
+ public func paramSymbols() -> [GraphSymbol] {
+ return [GraphSymbol]()
+ }
+
+ internal func cpu() {
+ for i in 0...stride))
+ let matrixInfoBuffer = engine.GPUDevice?.makeBuffer(bytes: &matrixInfo,
+ length: MemoryLayout.size)
+ guard matrixInfoBuffer != nil else {
+ SerranoLogging.errorLogging(message: "Failed to create matrix Info Buffer",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ SerranoLogging.stdLogging(message: "Allocated a Metal buffer [\(matrixInfoBuffer!.length) bytes] requested for count matrixInfo \(matrixInfo) by operator \(self.operatorLabel)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+ matrixInfoBuffers.append(matrixInfoBuffer!)
+ }
+ resourcePrepareGroup.leave()
+ }
+
+ resourcePrepareGroup.wait()
+
+ // encoder for each computation
+ for bufferIndex in 0..
+using namespace metal;
+
+
+typedef struct
+{
+ uint M; // number of rows in A
+ uint N; // number of cols in B
+ uint K; // number of cols in A, number of rows in B
+ ushort stride; // Element stride in bytes
+} MatrixDimInfo;
+
+kernel void MatrixMult_Single(constant float* input_A [[ buffer(0) ]],
+ constant float* input_B [[ buffer(1) ]], // should be transposed
+ const device float* C [[ buffer(2) ]],
+ constant MatrixDimInfo& dims [[ buffer(3) ]],
+ uint2 gid [[ thread_position_in_grid ]]) {
+ uint M = dims.M;
+ uint N = dims.N;
+ uint K = dims.K;
+ ushort stride = dims.stride;
+ // check boundary
+ if (gid.x >= M || gid.y >= N) return;
+
+ device float* c_reader = (device float*)((device char*)C + gid.x * N * stride + gid.y * stride);
+ c_reader[0] = 0.0f;
+
+ // small case
+ if (K < 4) {
+ constant float* a_reader = (constant float*)((constant char*)input_A + gid.x * K * stride);
+ constant float* b_reader = (constant float*)((constant char*)input_B + gid.y * K * stride);
+ ushort i = 0;
+ while (i < K) {
+ c_reader[0] += a_reader[i] * b_reader[i];
+ i++;
+ }
+ return;
+ }
+
+ // regular case, each time read 4 elements
+ constant float4* a_reader = (constant float4*)((constant char*)input_A + gid.x * K * stride);
+ constant float4* b_reader = (constant float4*)((constant char*)input_B + gid.y * K * stride);
+ uint align_bound = K / 4;
+ ushort i = 0;
+ float4 result;
+ while (i < align_bound) {
+ result = a_reader[i] * b_reader[i];
+ c_reader[0] += result.x;
+ c_reader[0] += result.y;
+ c_reader[0] += result.z;
+ c_reader[0] += result.w;
+ i++;
+ }
+
+ // rest
+ if (K % 4 != 0) {
+ constant float* a_reader_p = (constant float*)((constant char*)input_A + gid.x * K * stride);
+ constant float* b_reader_p = (constant float*)((constant char*)input_B + gid.y * K * stride);
+ i = align_bound * 4;
+ while (i < K) {
+ c_reader[0] += a_reader_p[i] * b_reader_p[i];
+ i++;
+ }
+ }
+}
+
+
+kernel void MatrixMult_submatrix(constant float* input_A [[ buffer(0) ]], // should be transposed for convenience
+ constant float* input_B [[ buffer(1) ]],
+ const device float* C [[ buffer(2) ]],
+ constant MatrixDimInfo& dims [[ buffer(3) ]],
+ uint2 gid [[ thread_position_in_grid ]]) {
+ uint M = dims.M;
+ uint N = dims.N;
+ uint K = dims.K;
+ ushort stride = dims.stride;
+
+ const ushort SUBMATRIX_SIZE = 4;
+
+ // output element start position
+ uint2 gid_out = uint2(gid.x * SUBMATRIX_SIZE, gid.y * SUBMATRIX_SIZE); // times 4
+
+ // check boundary
+ if (gid_out.x >= M || gid_out.y >= N) return;
+
+ ushort row_bound = min(gid_out.x + SUBMATRIX_SIZE, M) - gid_out.x;
+ ushort col_bound = min(gid_out.y + SUBMATRIX_SIZE, N) - gid_out.y;
+
+ float4x4 output_c_m = float4x4(0.0f);
+ constant float4* a_reader = (constant float4*)((constant char*)input_A + gid_out.x * stride);
+ constant float4* b_reader = (constant float4*)((constant char*)input_B + gid_out.y * stride);
+ for (uint i = 0; i < K; i++) { // loop block
+ output_c_m[0] += a_reader[0].x * b_reader[0];
+ output_c_m[1] += a_reader[0].y * b_reader[0];
+ output_c_m[2] += a_reader[0].z * b_reader[0];
+ output_c_m[3] += a_reader[0].w * b_reader[0];
+
+ a_reader = (constant float4*)((constant char*)a_reader + M * stride);
+ b_reader = (constant float4*)((constant char*)b_reader + N * stride);
+ }
+
+
+ // assign
+ device float* c_reader = (device float*)((device char*)C + gid_out.x * N * stride + gid_out.y * stride);
+ // reset boundary checker
+ for (int out_row_index = 0; out_row_index < row_bound; out_row_index++) {
+ for (int out_col_index = 0; out_col_index < col_bound; out_col_index++) {
+// c_reader[out_col_index] = output_c[out_row_index*SUBMATRIX_SIZE + out_col_index];
+ c_reader[out_col_index] = output_c_m[out_row_index][out_col_index];
+ }
+ // skip row
+ c_reader = (device float*)((device char*)c_reader + N * stride);
+ }
+}
+
diff --git a/Source/Serrano/operators/basic/matrix_mult_op.swift b/Source/Serrano/operators/basic/matrix_mult_op.swift
new file mode 100644
index 0000000..f806b66
--- /dev/null
+++ b/Source/Serrano/operators/basic/matrix_mult_op.swift
@@ -0,0 +1,606 @@
+//
+// dot_product_op.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 6/16/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import Accelerate
+import Metal
+#if !((arch(i386) || arch(x86_64)) && os(iOS)) // prevent build error on simulator
+import MetalPerformanceShaders
+#endif
+
+/// This struct corresponds to the `MatrixDimInfo` struct in file 'matrix_mult_op.metal'
+public struct MatrixDimInfo {
+ var M: MetalUInt // number of rows in A
+ var N: MetalUInt // number of cols in B
+ var K: MetalUInt // number of cols in A, number of rows in B
+ var stride: MetalUShort // element stride in bytes
+}
+
+
+/// Two kernels
+///
+/// - Single: Caclulate a single element each thread
+/// - SubMatrix: Caculate a submatrix each thread
+public enum MatrixMultKernel {
+ case Single
+ case SubMatrix
+}
+
+
+/**
+matrix multiplication.
+
+## Transpose input tensors
+Opertor's attributes `transposeA` and `transposeB` indicating if tranposing input tensors before doing calculation.
+And if any or both of them are set to `true`, all caulcation and input/output validation will be doing __after__ transposing.
+
+## Metal performance shader support
+By default, operator tries to use [MPSMatrixMultiplication](https://developer.apple.com/documentation/metalperformanceshaders/mpsmatrixmultiplication) in `MetalPerformanceShaders`.
+But on some devices which [do not support `MetalPerformanceShaders`](https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf), we use self kernel.
+
+## Multiple input
+This operator could takein multiple input. Currently, it support multiple input `A` and single input `B`.
+If `inputTensors` contains more than 2 elements, operator will view last element as input `B` and all previous element as input `A`s.
+
+*/
+public class MatrixMultOperator: ComputableOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// The submatrix size in Metal calcualtion.
+ /// Should be the same as `SUBMATRIX_SIZE` in `matrix_mult_op.metal`
+ public static let METAL_SUBMATRIX_SIZE = 4
+
+ /// Operator label. Conforms to `ComputableOperator`
+ public var operatorLabel: String
+
+ /// This operator does not operator on GPU. Conforms to `ComputableOperator`
+ public var metalKernelFuncLabel:String = "MatrixMult"
+
+ /// Conforms to `ComputableOperator`
+ public var computationDelegate: OperatorCalculationDelegate?
+
+ /// Conforms to `ComputableOperator`
+ public var inputTensors: [Tensor]?
+
+ /// Conforms to `ComputableOperator`
+ public var outputTensors: [Tensor]?
+
+ /// Whether transpose inputA before calculation
+ public var transposeA: Bool
+
+ /// Whether transpose inputB before calcualtion
+ public var transposeB: Bool
+
+ /// If `true`, operator will not call `inputOutputTensorsCheck()` before doing calculation.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ public var disableInputOutputCheck: Bool = false
+
+ /// Indicate if this operator would do paramter update.
+ public var trainable: Bool = false
+
+ /// Kernel to choose
+ public var kernel: MatrixMultKernel
+
+ /// The mapping type of this operator.
+ /// `Constant` for this operator.
+ public var mapType: OperatorMappingType {
+ get {
+ return OperatorMappingType.Constant
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializer
+
+ public init(operatorLabel: String = "MatrixMultOperator",
+ computationDelegate: OperatorCalculationDelegate? = nil,
+ transposeA: Bool = false, transposeB: Bool = false,
+ inputTensors: [Tensor]? = nil, outputTensors: [Tensor]? = nil,
+ kernel: MatrixMultKernel = MatrixMultKernel.Single,
+ disableInputOutputCheck: Bool = false) {
+ self.operatorLabel = operatorLabel
+ self.computationDelegate = computationDelegate
+ self.inputTensors = inputTensors
+ self.outputTensors = outputTensors
+ self.transposeA = transposeA
+ self.transposeB = transposeB
+ self.kernel = kernel
+ self.disableInputOutputCheck = disableInputOutputCheck
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Conforms to `ComputableOperator`
+
+ /// Check the input shapes. Following same rule of matrix multiplication.
+ ///
+ /// - Note: This function will transpose shape first and then calcualte output shape.
+ ///
+ /// - Parameter shapes: input shapes
+ /// - Returns: return shapes
+ public func outputShape(shapeArray shapes: [TensorShape]) -> [TensorShape]? {
+ // >=2 shapes
+ guard shapes.count >= 2 else {
+ SerranoLogging.errorLogging(message: "Operator \(self.operatorLabel) expect more than 2 shapes, given \(shapes.count)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ var shapeAs = shapes[0.. (check: Bool, msg: String) {
+ // not nil inputs
+ guard self.inputTensors != nil else {
+ return (false, "Input tensors are nil")
+ }
+
+ // >=2 input tensors
+ guard self.inputTensors!.count >= 2 else {
+ return (false, "Invalid input tneosr. Requires more than 2, given \(self.inputTensors!.count)")
+ }
+
+ // output not nil
+ guard self.outputTensors != nil else {
+ return (false, "Output tensors are nil")
+ }
+
+ // output tensors count match
+ guard self.outputTensors!.count == self.inputTensors!.count - 1 else {
+ return (false, "Invalid number of output tensors. Requires \(self.inputTensors!.count - 1), given \(self.inputTensors!.count)")
+ }
+
+ // check dimension
+ let inputShapes = self.inputTensors!.map { $0.shape }
+ let checkResult = self.outputShape(shapeArray: inputShapes)
+ guard checkResult != nil else {
+ return (false, "Invalid input tneosrs. See logs for details.")
+ }
+
+ // outptu dimension
+ let outputShapes = self.outputTensors!.map {$0.shape}
+ for (outShape, checkShape) in zip(outputShapes, checkResult!) {
+ guard outShape.shapeArray == checkShape.shapeArray else {
+ return (false, "Invalid output tensor. Expecting shape \(checkShape.shapeArray), given \(outShape.shapeArray)")
+ }
+ }
+
+ return (true, "")
+ }
+
+
+ /// Compute sync
+ ///
+ /// - Parameter computationMode: mode
+ public func compute(_ computationMode: OperatorComputationMode = SerranoEngine.configuredEngine.defaultComputationMode) {
+ // check
+ if self.disableInputOutputCheck {
+ let (pass, msg) = self.inputOutputTensorsCheck()
+ guard pass else {
+ SerranoLogging.errorLogging(message: "Operator \(self.operatorLabel) calculation aborted cause invalid input tensors or output tensors: \(msg)", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ }
+
+ self.computationDelegate?.operatorWillBeginComputation(self)
+
+ switch computationMode {
+ case .GPU:
+ if !SerranoEngine.configuredEngine.hasAvailableGPU() {
+ SerranoLogging.warningLogging(message: "Serrano Engine has no available configured GPU device. Use CPU doing calculation instead.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ self.cpu()
+ } else {
+ self.gpu()
+ }
+ case .CPU:
+ self.cpu()
+ case .Auto:
+ if self.outputTensors![0].count > 4000000 && SerranoEngine.configuredEngine.hasAvailableGPU() {
+ self.gpu()
+ } else {
+ self.cpu()
+ }
+ }
+ self.computationDelegate?.operatorDidEndComputation(self, outputTensors: self.outputTensors!)
+ }
+
+
+ /// Compute async
+ ///
+ /// - Parameter computationMode: mode
+ public func computeAsync(_ computationMode: OperatorComputationMode = SerranoEngine.configuredEngine.defaultComputationMode) {
+ // delegation check
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.compute(computationMode)
+ }
+ }
+
+ /// Calulate grads sync.
+ /// All unary operator return grads tensor with same number and shape as attribute `inputTensors`.
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ /// - upGrds: upGrds
+ /// - Returns: return grads tensor
+ public func gradCompute(_ computationMode: OperatorComputationMode) -> [String: DataSymbolSupportedDataType] {
+ //TODO: Implementation
+ fatalError("Not implemented")
+ }
+
+ /// Cal grads async
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ /// - upGrds: upGrds
+ public func gradComputAsync(_ computationMode: OperatorComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ _ = self.gradCompute(computationMode)
+ }
+ }
+
+ /// Update params if possible.
+ /// No update parameters for binary operators.
+ ///
+ /// - Parameters:
+ /// - grads: grads tensor list
+ /// - LR: learning rate
+ public func updateParams(grads: [Tensor], LR: Float) {
+ return
+ }
+
+ /// Do nothing
+ public func bindParamSymbols(_ symbols: [GraphSymbol]) {
+ // DO NOTHING
+ }
+
+ /// This operator has no parameters.
+ ///
+ /// - Returns: An empty array
+ public func paramSymbols() -> [GraphSymbol] {
+ return [GraphSymbol]()
+ }
+
+
+ /// Get M, N, K attributes
+ ///
+ /// - Returns: M, N, K
+ internal func MNKFetch() -> (M: Int, N: Int, K: Int) {
+ let tensorA = self.inputTensors![0]
+ let tensorB = self.inputTensors![1]
+
+ var M = tensorA.shape.shapeArray[0]
+ if self.transposeA {
+ M = tensorA.shape.shapeArray[1]
+ }
+ var N = tensorB.shape.shapeArray[1]
+ if self.transposeB {
+ N = tensorB.shape.shapeArray[0]
+ }
+ var K = tensorA.shape.shapeArray[1]
+ if self.transposeA {
+ K = tensorA.shape.shapeArray[0]
+ }
+ return (M, N, K)
+ }
+
+ /// Get M, N, K attributes
+ ///
+ /// - Returns: M, N, K
+ internal func MNKFetch(tensorA: Tensor, tensorB: Tensor) -> (M: Int, N: Int, K: Int) {
+ var M = tensorA.shape.shapeArray[0]
+ if self.transposeA {
+ M = tensorA.shape.shapeArray[1]
+ }
+ var N = tensorB.shape.shapeArray[1]
+ if self.transposeB {
+ N = tensorB.shape.shapeArray[0]
+ }
+ var K = tensorA.shape.shapeArray[1]
+ if self.transposeA {
+ K = tensorA.shape.shapeArray[0]
+ }
+ return (M, N, K)
+ }
+
+ /// Use `BLAS` `cblas_sgemm` to do calculation
+ internal func cpu() {
+ let workGroup = DispatchGroup()
+ let inputB = self.inputTensors!.last!
+ let inputBAddress = inputB.contentsAddress
+
+ for (input, output) in zip(self.inputTensors!, self.outputTensors!) {
+ workGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ let inputAAddress = input.contentsAddress
+ let outCAddress = output.contentsAddress
+
+ let (M, N, K) = self.MNKFetch(tensorA: input, tensorB: inputB)
+
+ let lda = Int32(input.shape.shapeArray[1])
+ let ldb = Int32(inputB.shape.shapeArray[1])
+ let ldc = Int32(output.shape.shapeArray[1])
+
+ cblas_sgemm(CblasRowMajor, cblasTrans(self.transposeA), cblasTrans(self.transposeB), Int32(M), Int32(N), Int32(K),
+ 1.0, inputAAddress, lda, inputBAddress, ldb, 0.0, outCAddress, ldc)
+ workGroup.leave()
+ }
+ }
+ workGroup.wait()
+ }
+
+
+ /// This method choose proper kernel or MPS to do calculation
+ internal func gpu() {
+ // Use MPS if possible
+ if MetalHardwareChecker.supportMPS() {
+ self.gpu_kernel_MPS()
+ return
+ }
+ // choose kernel
+ if self.transposeA && !self.transposeB {
+ self.gpu_kernel_submatrix()
+ } else {
+ self.gpu_kernel_single()
+ }
+ }
+
+ /// Do calculation of inputA and transpoedB.
+ /// `transposedInputB` is supposed already transposed
+ ///
+ /// - Parameters:
+ /// - inputABuffer: inputA
+ /// - inputBBufferTransposed: transposedInputB
+ /// - outputCBuffer: outputCBuffer
+ /// - dimInfo: dimInfo
+ /// - kernel: kernel
+ internal func gpu_single(inputABuffer: MTLBufferResource, inputBBufferTransposed: MTLBufferResource, outputCBuffer: MTLBufferResource,
+ dimInfo: inout MatrixDimInfo, kernel: MTLComputePipelineState) {
+ let commandBuffer = SerranoEngine.configuredEngine.serranoCommandQueue?.makeCommandBuffer()
+ guard commandBuffer != nil else {
+ fatalError("[Serrano] Failed to make new command buffer.")
+ }
+
+ // Encoders.
+ let encoder = commandBuffer!.makeComputeCommandEncoder()
+ encoder.setComputePipelineState(kernel)
+ encoder.setBuffer(inputABuffer.buffer, offset: inputABuffer.offset, at: 0)
+ encoder.setBuffer(inputBBufferTransposed.buffer, offset: inputBBufferTransposed.offset, at: 1)
+ encoder.setBuffer(outputCBuffer.buffer, offset: outputCBuffer.offset, at: 2)
+ encoder.setBytes(&dimInfo, length: MemoryLayout.stride, at: 3)
+
+ /// Calculate grid
+ let threadsPerThreadgroup = MTLSizeMake(kernel.threadExecutionWidth,
+ kernel.maxTotalThreadsPerThreadgroup / kernel.threadExecutionWidth,
+ 1)
+ let threadgroupsPerGrid = MTLSizeMake((Int(dimInfo.M) + threadsPerThreadgroup.width - 1) / threadsPerThreadgroup.width,
+ (Int(dimInfo.N) + threadsPerThreadgroup.height - 1) / threadsPerThreadgroup.height,
+ 1)
+ encoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup)
+ encoder.endEncoding()
+
+ if !SerranoLogging.release {
+ SerranoLogging.stdLogging(message: "Dispatch group configured with threadgroupsPerGrid: \(threadgroupsPerGrid), threadsPerThreadgroup: \(threadsPerThreadgroup) requested by operator \(self.operatorLabel)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+ }
+
+
+ // commit command buffer
+ commandBuffer!.commit()
+ commandBuffer!.waitUntilCompleted()
+ }
+
+ internal func gpu_kernel_single() {
+ let (kernel, info) = SerranoEngine.configuredEngine.loadGPUKernel(kernelLabel: "MatrixMult_Single")
+ guard kernel != nil else {
+ fatalError("[Serrano] Failed to load kernel MatrixMult_Single. Info: \(info)")
+ }
+
+ // transpose B process
+ var inputBTransposed = self.inputTensors!.last!
+ if !self.transposeB {
+ let transposeB = SerranoResourceManager.globalManager.allocateUnamangedTensor(inputBTransposed.shape.transposed())
+ let transOp = TransposeOperator(inputTensors: [inputBTransposed], outputTensors: [transposeB])
+ transOp.disableInputOutputCheck = true
+ transOp.compute(.GPU)
+ inputBTransposed = transposeB
+ }
+ let inputBBufferTransposed = SerranoResourceManager.globalManager.allocateMTLBufferResource(inputBTransposed)
+
+ // do calcualtion
+ let workGroup = DispatchGroup()
+ if self.transposeA {
+ for (inputA, output) in zip(self.inputTensors![0...stride))
+ self.gpu_single(inputABuffer: buffers[0], inputBBufferTransposed: inputBBufferTransposed,
+ outputCBuffer: buffers[1], dimInfo: &info, kernel: kernel!)
+ workGroup.leave()
+ }
+ }
+ } else {
+ for (inputA, output) in zip(self.inputTensors![0...stride))
+ self.gpu_single(inputABuffer: buffers[0], inputBBufferTransposed: inputBBufferTransposed,
+ outputCBuffer: buffers[1], dimInfo: &info, kernel: kernel!)
+ workGroup.leave()
+ }
+ }
+ }
+
+ workGroup.wait()
+ }
+
+
+ /// Do matrix multiplication with submatrix kernel.
+ ///
+ /// - Parameters:
+ /// - inputATransposeBuffer: transposed A buffer
+ /// - inputBBuffer: inputBBuffer
+ /// - outputCBuffer: outputCBuffer
+ /// - dimInfo: dimInfo
+ /// - kernel: kernel
+ internal func gpu_submatrix(inputATransposeBuffer: MTLBufferResource, inputBBuffer: MTLBufferResource,
+ outputCBuffer: MTLBufferResource,
+ dimInfo: inout MatrixDimInfo, kernel: MTLComputePipelineState) {
+ let commandBuffer = SerranoEngine.configuredEngine.serranoCommandQueue?.makeCommandBuffer()
+ guard commandBuffer != nil else {
+ fatalError("[Serrano] Failed to make new command buffer.")
+ }
+
+ // Encoders.
+ let encoder = commandBuffer!.makeComputeCommandEncoder()
+ encoder.setComputePipelineState(kernel)
+ encoder.setBuffer(inputATransposeBuffer.buffer, offset: inputATransposeBuffer.offset, at: 0)
+ encoder.setBuffer(inputBBuffer.buffer, offset: inputBBuffer.offset, at: 1)
+ encoder.setBuffer(outputCBuffer.buffer, offset: outputCBuffer.offset, at: 2)
+ encoder.setBytes(&dimInfo, length: MemoryLayout.stride, at: 3)
+
+ /// Calculate grid
+ let threadsPerThreadgroup = MTLSizeMake(MatrixMultOperator.METAL_SUBMATRIX_SIZE,
+ MatrixMultOperator.METAL_SUBMATRIX_SIZE,
+ 1)
+ let threadgroupsPerGrid = MTLSizeMake((Int(dimInfo.M) + threadsPerThreadgroup.width - 1) / threadsPerThreadgroup.width,
+ (Int(dimInfo.N) + threadsPerThreadgroup.height - 1) / threadsPerThreadgroup.height,
+ 1)
+ encoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup)
+ encoder.endEncoding()
+
+ if !SerranoLogging.release {
+ SerranoLogging.stdLogging(message: "Dispatch group configured with threadgroupsPerGrid: \(threadgroupsPerGrid), threadsPerThreadgroup: \(threadsPerThreadgroup) requested by operator \(self.operatorLabel)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+ }
+
+ // commit command buffer
+ commandBuffer!.commit()
+ commandBuffer!.waitUntilCompleted()
+ }
+
+ /// - Note: There's no any transposing processing in this function cause
+ /// in function `gpu()` it only dispatches suitable inputs to this function.
+ internal func gpu_kernel_submatrix() {
+ let (kernel, info) = SerranoEngine.configuredEngine.loadGPUKernel(kernelLabel: "MatrixMult_submatrix")
+ guard kernel != nil else {
+ fatalError("[Serrano] Failed to load kernel MatrixMult_Single. Info: \(info)")
+ }
+
+ let inputBBuffer = SerranoResourceManager.globalManager.allocateMTLBufferResource(self.inputTensors!.last!)
+
+ // do calcualtion
+ let workGroup = DispatchGroup()
+
+ for (inputA, output) in zip(self.inputTensors![0...stride))
+ self.gpu_submatrix(inputATransposeBuffer: buffers[0], inputBBuffer: inputBBuffer, outputCBuffer: buffers[1],
+ dimInfo: &info, kernel: kernel!)
+ workGroup.leave()
+ }
+ }
+ workGroup.wait()
+ }
+
+ internal func gpu_kernel_MPS() {
+ #if !((arch(i386) || arch(x86_64)) && os(iOS)) && !(arch(x86_64))// prevent build error on simulator
+ let tensors = [self.inputTensors![0], self.inputTensors![1], self.outputTensors![0]]
+
+ //TODO: IMPLEMENT TRANSPOSE ADAPTIVE CODE
+ // Here allcoate unmanaged, cause we cannot use buffer offset in MPS
+ let buffers = SerranoResourceManager.globalManager.allocateUnmanagedMTLBuffers(tensors)
+
+ // get matrix
+ var matrix = [MPSMatrix]()
+ for (tensor, buffer) in zip(tensors, buffers) {
+ let descript = MPSMatrixDescriptor(dimensions: tensor.shape.shapeArray[0], columns: tensor.shape.shapeArray[1],
+ rowBytes: tensor.shape.shapeArray[1] * MemoryLayout.stride,
+ dataType: .float32)
+ matrix.append(MPSMatrix(buffer: buffer, descriptor: descript))
+ }
+
+ //kernel
+ let kernel = MPSMatrixMultiplication(device: SerranoEngine.configuredEngine.GPUDevice!,
+ transposeLeft: self.transposeA, transposeRight: self.transposeB,
+ resultRows: tensors[2].shape.shapeArray[0], resultColumns: tensors[2].shape.shapeArray[1],
+ interiorColumns: tensors[0].shape.shapeArray[1], alpha: 1, beta: 0)
+
+ let commandBuffer = SerranoEngine.configuredEngine.serranoCommandQueue!.makeCommandBuffer()
+ kernel.encode(commandBuffer: commandBuffer, leftMatrix: matrix[0], rightMatrix: matrix[1], resultMatrix: matrix[2])
+ commandBuffer.commit()
+ let startTime = CFAbsoluteTimeGetCurrent()
+ commandBuffer.waitUntilCompleted()
+ let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
+
+ SerranoLogging.stdLogging(message: "Executed GPU calcualtion in \(timeElapsed) seconds.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+ #endif
+ }
+
+}
diff --git a/Source/Serrano/operators/basic/reduce/reduce_op.metal b/Source/Serrano/operators/basic/reduce/reduce_op.metal
new file mode 100644
index 0000000..a539904
--- /dev/null
+++ b/Source/Serrano/operators/basic/reduce/reduce_op.metal
@@ -0,0 +1,15 @@
+//
+// reduce_op.metal
+// serrano
+//
+// Created by ZHONGHAO LIU on 6/27/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+#include
+using namespace metal;
+
+
+namespace serrano_ios {
+
+}
diff --git a/Source/Serrano/operators/basic/reduce/reduce_op.swift b/Source/Serrano/operators/basic/reduce/reduce_op.swift
new file mode 100644
index 0000000..b29c629
--- /dev/null
+++ b/Source/Serrano/operators/basic/reduce/reduce_op.swift
@@ -0,0 +1,641 @@
+//
+// reduce_op.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 6/27/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import Metal
+import Accelerate
+import Dispatch
+
+
+/**
+The abstract class for all reduce operators. Should not be used directly.
+A reduce operator do some aggregate calculation along given axes.
+An example given by TensorFlow is [here](https://www.tensorflow.org/api_docs/python/tf/reduce_sum).
+*/
+public class ReduceOperator: ComputableOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ public var computationDelegate: OperatorCalculationDelegate?
+
+ public var metalKernelFuncLabel: String
+
+ public var operatorLabel: String
+
+ public var inputTensors: [Tensor]?
+
+ public var outputTensors: [Tensor]?
+
+ /// The axes to do the computation
+ public var axis: [Int] {
+ didSet {
+ self.axis = Array(Set(self.axis)) // remove duplicates
+ }
+ }
+
+ /// Indicate if keep dimensions in result tensor.
+ /// This just affects result tensor's `shape` attributes.
+ /// Default `false`
+ public var keepDim: Bool = false
+
+ /// The element compuation block in CPU mode.
+ /// In most cases, subclass should just override this part in `init` method instead overriding the whole `cpu()` method.
+ /// The firat pointer is the input tensor,
+ //// the second is the output tensor
+ public lazy var cpuComputeBlock: (Tensor, Tensor, [Int]) -> Void = { (inputTensor: Tensor, outputTensor: Tensor, axis: [Int]) -> Void in
+ print("NEED OVERLOAD")
+ }
+
+ /// If `true`, operator will not check the `upGrads`'s shape.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ /// Cases like auto generated differentiation graph.
+ public var disableUpGradShapeCheck: Bool = false
+
+ /// If `true`, operator will not call `inputOutputTensorsCheck()` before doing calculation.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ public var disableInputOutputCheck: Bool = false
+
+ /// Indicate if this operator would do paramter update.
+ ///
+ /// - Note: All `UnaryOperators` are not trainable.
+ public var trainable: Bool = false
+
+ /// The mapping type of this operator.
+ /// `OneToOne` for this operator.
+ public var mapType: OperatorMappingType {
+ get {
+ return OperatorMappingType.OneToOne
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+ init(computationDelegate: OperatorCalculationDelegate?,
+ operatorLabel: String = "NEED OVERRIDE",
+ metalKernelFuncLabel kernelLabel: String = "NEED OVERRIDE",
+ inputTensors: [Tensor]?, outputTensors: [Tensor]?,
+ axis: [Int]) {
+ self.computationDelegate = computationDelegate
+ self.operatorLabel = operatorLabel
+ self.inputTensors = inputTensors
+ self.outputTensors = outputTensors
+ self.metalKernelFuncLabel = kernelLabel
+ self.axis = Array(Set(axis))
+ self.cpuComputeBlock = { (inputTensor: Tensor, outputTensor: Tensor, axis: [Int]) -> Void in
+ print("NEED OVERLOAD")
+ }
+ }
+
+
+ /// Conenience init.
+ ///
+ /// - Parameters:
+ /// - inputTensors: inputTensors description
+ /// - outputTensors: outputTensors description
+ /// - axis: axis description
+ public convenience required init(inputTensors: [Tensor]? = nil, outputTensors: [Tensor]? = nil, axis: [Int]) {
+ self.init(computationDelegate: nil, inputTensors: inputTensors, outputTensors: outputTensors, axis: axis)
+ }
+
+
+ /// Conenience init.
+ ///
+ /// - Parameters:
+ /// - axis: axis
+ /// - keepDim: keepDim
+ public convenience init(axis: [Int], keepDim: Bool) {
+ self.init(computationDelegate: nil, inputTensors: nil, outputTensors: nil, axis: axis)
+ self.keepDim = keepDim
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+
+ /// Check if for each input shape operator could do reduce operation with `axis` attribute value.
+ ///
+ /// - Parameter shapes: shapes description
+ /// - Returns: return value description
+ public func outputShape(shapeArray shapes: [TensorShape]) -> [TensorShape]? {
+ var outShapes = [TensorShape]()
+
+ // empty input warning
+ if shapes.count == 0 {
+ SerranoLogging.warningLogging(message: "The input shapes contains no element.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return outShapes
+ }
+
+ for shape in shapes {
+ // check rank
+ guard shape.rank >= self.axis.count else {
+ SerranoLogging.errorLogging(message: "Input shape [\(shape)] is not valid on target axis: \(self.axis)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+
+ var outputShapeArray = shape.shapeArray
+ // ax dim value check
+ for ax in self.axis {
+ guard ax < outputShapeArray.count && ax >= 0 else {
+ SerranoLogging.errorLogging(message: "Input shape [\(shape)] is not valid on target axis: \(self.axis)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+ outputShapeArray[ax] = 1
+ }
+
+ // not keeping dim, del dim
+ if !self.keepDim {
+ outputShapeArray = outputShapeArray
+ .enumerated()
+ .filter{ !self.axis.contains($0.offset) }
+ .map { $0.element }
+ }
+ outShapes.append(TensorShape(dataType: shape.dataType, shape: outputShapeArray))
+ }
+
+ return outShapes
+ }
+
+
+ /// Validate shapes`inputTensors` and `outputTensors`.
+ ///
+ /// - Returns: check passing and message if not passing
+ public func inputOutputTensorsCheck() -> (check: Bool, msg: String) {
+ // input not nil
+ guard self.inputTensors != nil else {
+ return (false, "Input tensors are nil.")
+ }
+
+ // output not nil
+ guard self.outputTensors != nil else {
+ return (false, "Output tensors are nil.")
+ }
+
+ // count
+ guard self.inputTensors!.count == self.outputTensors!.count else {
+ return (false, "Input and output tensors should have same amount of tensors. " +
+ "Input: \(self.inputTensors!.count), output: \(self.outputTensors!.count).")
+ }
+
+ // check input shapes
+ let inputShapes = self.inputTensors!.map { $0.shape }
+ let outputShapesValid = self.outputShape(shapeArray: inputShapes)
+ guard outputShapesValid != nil else {
+ return (false, "Input tensors' shapes are not valid. Check log for detail.")
+ }
+
+ // check output shapes
+ let outputShapes = self.outputTensors!.map { $0.shape }
+ for i in 0.. [String: DataSymbolSupportedDataType] {
+ //TODO: Implementation
+ fatalError("Not implemented")
+ }
+
+ /// Cal grads async
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ public func gradComputAsync(_ computationMode: OperatorComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ _ = self.gradCompute(computationMode)
+ }
+ }
+
+ /// Update params if possible.
+ /// No update parameters for binary operators.
+ ///
+ /// - Parameters:
+ /// - grads: grads tensor list
+ /// - LR: learning rate
+ public func updateParams(grads: [Tensor], LR: Float) {
+ return
+ }
+
+ /// This operator has no parameters. Do nothing
+ ///
+ public func bindParamSymbols(_ symbols: [GraphSymbol]) {
+
+ }
+
+ /// This operator has no parameters.
+ ///
+ /// - Returns: An empty array
+ public func paramSymbols() -> [GraphSymbol] {
+ return [GraphSymbol]()
+ }
+
+ internal func cpu() {
+ let workGroup = DispatchGroup()
+ for tensorIndex in 0.. Void in
+ let op = ReduceOperator(axis: [Int]())
+ op.keepDim = true
+
+ var inputAddress = inputTensor.contentsAddress
+ var outptuAddress: UnsafeMutablePointer
+ var inputShape = inputTensor.shape
+ var intermediateTensor: Tensor = inputTensor
+ var nextTensor: Tensor = outputTensor
+
+ let sortedAxis = axis.sorted()
+ for (axIndex, ax) in sortedAxis.enumerated() {
+ // decide output address
+ if axIndex != sortedAxis.count - 1 {
+ // intermediate
+ op.axis = [ax]
+ let nextShape = op.outputShape(shapeArray: [inputShape])!.first!
+ nextTensor = Tensor(repeatingValue: 0.0, tensorShape: nextShape)
+ outptuAddress = nextTensor.contentsAddress
+ } else {
+ nextTensor = outputTensor
+ }
+ outptuAddress = nextTensor.contentsAddress
+
+ // do reduce on intermediateTensor and store result to newTensor
+ let preDimCount = intermediateTensor.shape.shapeArray.prefix(upTo: ax).reduce(1, *)
+ let nextEntryCount = intermediateTensor.shape.shapeArray.suffix(from: ax+1).reduce(1, *)
+ let entryCount = intermediateTensor.shape.shapeArray.suffix(from: ax).reduce(1, *)
+ let axDim = intermediateTensor.shape.shapeArray[ax]
+ for preDimIndex in 0.. Void in
+ let op = ReduceOperator(axis: [Int]())
+ op.keepDim = true
+
+ var inputAddress = inputTensor.contentsAddress
+ var outptuAddress: UnsafeMutablePointer
+ var inputShape = inputTensor.shape
+ var intermediateTensor: Tensor = inputTensor
+ var nextTensor: Tensor = outputTensor
+
+ let sortedAxis = axis.sorted()
+ for (axIndex, ax) in sortedAxis.enumerated() {
+ // decide output address
+ if axIndex != sortedAxis.count - 1 {
+ // intermediate
+ op.axis = [ax]
+ let nextShape = op.outputShape(shapeArray: [inputShape])!.first!
+ nextTensor = Tensor(repeatingValue: 0.0, tensorShape: nextShape)
+ outptuAddress = nextTensor.contentsAddress
+ } else {
+ nextTensor = outputTensor
+ }
+ outptuAddress = nextTensor.contentsAddress
+
+ // do reduce on intermediateTensor and store result to newTensor
+ let preDimCount = intermediateTensor.shape.shapeArray.prefix(upTo: ax).reduce(1, *)
+ let nextEntryCount = intermediateTensor.shape.shapeArray.suffix(from: ax+1).reduce(1, *)
+ let entryCount = intermediateTensor.shape.shapeArray.suffix(from: ax).reduce(1, *)
+ let axDim = intermediateTensor.shape.shapeArray[ax]
+ for preDimIndex in 0.. Void in
+ let op = ReduceOperator(axis: [Int]())
+ op.keepDim = true
+
+ var inputAddress = inputTensor.contentsAddress
+ var outptuAddress: UnsafeMutablePointer
+ var inputShape = inputTensor.shape
+ var intermediateTensor: Tensor = inputTensor
+ var nextTensor: Tensor = outputTensor
+
+ let sortedAxis = axis.sorted()
+ for (axIndex, ax) in sortedAxis.enumerated() {
+ // decide output address
+ if axIndex != sortedAxis.count - 1 {
+ // intermediate
+ op.axis = [ax]
+ let nextShape = op.outputShape(shapeArray: [inputShape])!.first!
+ nextTensor = Tensor(repeatingValue: 0.0, tensorShape: nextShape)
+ outptuAddress = nextTensor.contentsAddress
+ } else {
+ nextTensor = outputTensor
+ }
+ outptuAddress = nextTensor.contentsAddress
+
+ // do reduce on intermediateTensor and store result to newTensor
+ let preDimCount = intermediateTensor.shape.shapeArray.prefix(upTo: ax).reduce(1, *)
+ let nextEntryCount = intermediateTensor.shape.shapeArray.suffix(from: ax+1).reduce(1, *)
+ let entryCount = intermediateTensor.shape.shapeArray.suffix(from: ax).reduce(1, *)
+ let axDim = intermediateTensor.shape.shapeArray[ax]
+ for preDimIndex in 0.. Void in
+ let op = ReduceOperator(axis: [Int]())
+ op.keepDim = true
+
+ var inputAddress = inputTensor.contentsAddress
+ var outptuAddress: UnsafeMutablePointer
+ var inputShape = inputTensor.shape
+ var intermediateTensor: Tensor = inputTensor
+ var nextTensor: Tensor = outputTensor
+
+ let sortedAxis = axis.sorted()
+ for (axIndex, ax) in sortedAxis.enumerated() {
+ // decide output address
+ if axIndex != sortedAxis.count - 1 {
+ // intermediate
+ op.axis = [ax]
+ let nextShape = op.outputShape(shapeArray: [inputShape])!.first!
+ nextTensor = Tensor(repeatingValue: 0.0, tensorShape: nextShape)
+ outptuAddress = nextTensor.contentsAddress
+ } else {
+ nextTensor = outputTensor
+ }
+ outptuAddress = nextTensor.contentsAddress
+
+ // do reduce on intermediateTensor and store result to newTensor
+ let preDimCount = intermediateTensor.shape.shapeArray.prefix(upTo: ax).reduce(1, *)
+ let nextEntryCount = intermediateTensor.shape.shapeArray.suffix(from: ax+1).reduce(1, *)
+ let entryCount = intermediateTensor.shape.shapeArray.suffix(from: ax).reduce(1, *)
+ let axDim = intermediateTensor.shape.shapeArray[ax]
+ for preDimIndex in 0.. Void in
+ let op = ReduceOperator(axis: [Int]())
+ op.keepDim = true
+
+ var inputAddress = inputTensor.contentsAddress
+ var outptuAddress: UnsafeMutablePointer
+ var inputShape = inputTensor.shape
+ var intermediateTensor: Tensor = inputTensor
+ var nextTensor: Tensor = outputTensor
+ var placeHolder: Float = 1.0
+
+ let sortedAxis = axis.sorted()
+ for (axIndex, ax) in sortedAxis.enumerated() {
+ // decide output address
+ if axIndex != sortedAxis.count - 1 {
+ // intermediate
+ op.axis = [ax]
+ let nextShape = op.outputShape(shapeArray: [inputShape])!.first!
+ nextTensor = Tensor(repeatingValue: 0.0, tensorShape: nextShape)
+ outptuAddress = nextTensor.contentsAddress
+ } else {
+ nextTensor = outputTensor
+ }
+ outptuAddress = nextTensor.contentsAddress
+
+ // do reduce on intermediateTensor and store result to newTensor
+ let preDimCount = intermediateTensor.shape.shapeArray.prefix(upTo: ax).reduce(1, *)
+ let nextEntryCount = intermediateTensor.shape.shapeArray.suffix(from: ax+1).reduce(1, *)
+ let entryCount = intermediateTensor.shape.shapeArray.suffix(from: ax).reduce(1, *)
+ let axDim = intermediateTensor.shape.shapeArray[ax]
+ for preDimIndex in 0.. [TensorShape]? {
+ return shapes
+ }
+
+ /// The `inputTensors` should not be `nil`.
+ ///
+ ///
+ public func inputOutputTensorsCheck() -> (check: Bool, msg: String) {
+ // inputTensors should not be `nil`
+ guard self.inputTensors != nil else {
+ return (false, "Operator \(self.operatorLabel) should have valid inputTensors.")
+ }
+
+ guard self.outputTensors != nil else {
+ return (false, "Operator \(self.operatorLabel) should have valid outputTensors.")
+ }
+
+ // if assigned outputTensors, check match
+ guard self.inputTensors!.count == self.outputTensors!.count else {
+ return (false, "Operator \(self.operatorLabel) should have same amount of input tensors and output tensors. " +
+ "Given \(self.inputTensors!.count) inputTensors and \(self.outputTensors!.count) outputTensors")
+ }
+
+ for i in 0...stride)
+ }
+ }
+
+
+ /// Compute Async
+ ///
+ /// - Parameters:
+ /// - tensors: tensors description
+ /// - computationMode: computationMode description
+ public func computeAsync(_ computationMode: OperatorComputationMode = SerranoEngine.configuredEngine.defaultComputationMode) {
+ OperatorUtils.delegateNilWarning(op: self, file: #file, function: #function, line: #line)
+ self.computationDelegate?.operatorWillBeginComputation(self)
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.compute(computationMode)
+ self.computationDelegate?.operatorDidEndComputation(self, outputTensors: self.outputTensors!)
+ }
+ }
+
+ /// Calulate grads sync.
+ /// Copy operator itself does not generate any grads. Should be ignored in gaph AD.
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ /// - Returns: return `upGrads` if not nil. Else return an empty array.
+ public func gradCompute(_ computationMode: OperatorComputationMode) -> [String: DataSymbolSupportedDataType] {
+ return [:]
+ }
+
+
+ /// Cal grads async
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ /// - upGrds: upGrds
+ public func gradComputAsync(_ computationMode: OperatorComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.computationDelegate?.operatorWillBeginGradsComputation(self)
+ let result = self.gradCompute(computationMode)
+ self.computationDelegate?.operatorDidEndGradsComputation(self, grads: result)
+ }
+ }
+
+
+ /// No updatable parameters.
+ /// This function just returns.
+ ///
+ /// - Parameters:
+ /// - grads: grads
+ /// - LR: LR
+ public func updateParams(grads: [Tensor], LR: Float) {
+ return
+ }
+
+ /// This operator has no parameters. Do nothing
+ ///
+ public func bindParamSymbols(_ symbols: [GraphSymbol]) {
+
+ }
+
+ /// This operator has no parameters.
+ ///
+ /// - Returns: An empty array
+ public func paramSymbols() -> [GraphSymbol] {
+ return [GraphSymbol]()
+ }
+}
diff --git a/Source/Serrano/operators/basic/unary/unary_op.metal b/Source/Serrano/operators/basic/unary/unary_op.metal
new file mode 100644
index 0000000..c0a782b
--- /dev/null
+++ b/Source/Serrano/operators/basic/unary/unary_op.metal
@@ -0,0 +1,264 @@
+//
+// unary_op.metal
+// serrano
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+#include
+using namespace metal;
+
+namespace serrano_ios {
+ kernel void Sin(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = sin(in_tensor[gid.x]);
+ }
+
+ kernel void Arcsin(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = asin(in_tensor[gid.x]);
+ }
+
+ kernel void Cos(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = cos(in_tensor[gid.x]);
+ }
+
+ kernel void Tan(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = tan(in_tensor[gid.x]);
+ }
+
+ kernel void Arctan(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = atan(in_tensor[gid.x]);
+ }
+
+ kernel void Abs(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = abs(in_tensor[gid.x]);
+ }
+
+ kernel void Degree(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = 180 / M_PI_F * in_tensor[gid.x] ;
+ }
+
+ kernel void Arccos(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = acos(in_tensor[gid.x]);
+ }
+
+ kernel void Radien(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = M_PI_F / 180 * in_tensor[gid.x];
+ }
+
+ kernel void Sinh(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = sinh(in_tensor[gid.x]);
+ }
+
+ kernel void Cosh(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = cosh(in_tensor[gid.x]);
+ }
+
+ kernel void Tanh(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = tanh(in_tensor[gid.x]);
+ }
+
+ kernel void Arctanh(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = atanh(in_tensor[gid.x]);
+ }
+
+ kernel void Arccosh(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = acosh(in_tensor[gid.x]);
+ }
+
+ kernel void Arcsinh(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = asinh(in_tensor[gid.x]);
+ }
+
+ kernel void Floor(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = floor(in_tensor[gid.x]);
+ }
+
+ kernel void Ceil(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = ceil(in_tensor[gid.x]);
+ }
+
+ kernel void Rint(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = rint(in_tensor[gid.x]);
+ }
+
+ kernel void Round(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = round(in_tensor[gid.x]);
+ }
+
+ kernel void Square(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = in_tensor[gid.x] * in_tensor[gid.x];
+ }
+
+ kernel void Rsqrt(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = rsqrt(in_tensor[gid.x]);
+ }
+
+ kernel void Sqrt(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = sqrt(in_tensor[gid.x]);
+ }
+
+ kernel void Log1p(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = log(in_tensor[gid.x] + 1);
+ }
+
+ kernel void Log2(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = log2(in_tensor[gid.x]);
+ }
+
+ kernel void Log10(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = log10(in_tensor[gid.x]);
+ }
+
+ kernel void Log(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = log(in_tensor[gid.x]);
+ }
+
+ kernel void Expm1(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = exp(in_tensor[gid.x]) - 1;
+ }
+
+ kernel void Exp(device float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint* count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]])
+ {
+ if (gid.x >= count[0]) return;
+ out_tensor[gid.x] = exp(in_tensor[gid.x]);
+ }
+}
diff --git a/Source/Serrano/operators/basic/unary/unary_op.swift b/Source/Serrano/operators/basic/unary/unary_op.swift
new file mode 100644
index 0000000..40cf048
--- /dev/null
+++ b/Source/Serrano/operators/basic/unary/unary_op.swift
@@ -0,0 +1,1506 @@
+//
+// unary_op.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 6/2/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import Dispatch
+import Metal
+import Accelerate
+
+/**
+ Abstract class define the standard unary operator working flow.
+ This class should not be used directly.
+ Any class inheritance this class is doing element-wise computation for input tensors.
+ */
+public class UnaryOperator: ComputableOperator {
+
+ /// Operator label. Conforms to `ComputableOperator`
+ public var operatorLabel: String = ""
+
+ /// This operator does not operator on GPU. Conforms to `ComputableOperator`
+ public var metalKernelFuncLabel:String
+
+ /// Conforms to `ComputableOperator`
+ public var computationDelegate: OperatorCalculationDelegate?
+
+ /// Conforms to `ComputableOperator`
+ public var inputTensors: [Tensor]?
+
+ /// Conforms to `ComputableOperator`
+ public var outputTensors: [Tensor]?
+
+ /// The element compuation block in CPU mode.
+ /// In most cases, subclass should just override this part in `init` method instead overriding the whole `cpu()` method.
+ /// The firat pointer is the input tensor,
+ //// the second is the output tensor
+ public var cpuElementComputationBlock: (Tensor, Tensor) -> Void
+
+
+ /// The grad compuation block.
+ /// parameter: inputTensors,
+ public var gradComputationBlock: ([Tensor], OperatorComputationMode) -> [DataSymbolSupportedDataType]
+
+ /// If `true`, operator will not check the `upGrads`'s shape.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ /// Cases like auto generated differentiation graph.
+ public var disableUpGradShapeCheck: Bool = false
+
+ /// If `true`, operator will not call `inputOutputTensorsCheck()` before doing calculation.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ public var disableInputOutputCheck: Bool = false
+
+ /// Indicate if this operator would do paramter update.
+ ///
+ /// - Note: All `UnaryOperators` are not trainable.
+ public var trainable: Bool = false
+
+ /// The mapping type of this operator.
+ /// `OneToOne` for this operator.
+ public var mapType: OperatorMappingType {
+ get {
+ return OperatorMappingType.OneToOne
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+ /// Designated init function
+ ///
+ /// - Parameters:
+ /// - label: label description
+ /// - delegate: delegate description
+ init(operatorLabel label: String,
+ cpuComputeBlock block: @escaping (Tensor, Tensor) -> Void ,
+ gradComputationBlock gradBlock: @escaping ([Tensor], OperatorComputationMode) -> [DataSymbolSupportedDataType],
+ metalKernelFuncLabel kernelLabel: String,
+ computationDelegate: OperatorCalculationDelegate?,
+ inputTensors: [Tensor]?,
+ outputTensors: [Tensor]?) {
+ self.operatorLabel = label
+ self.computationDelegate = computationDelegate
+ self.metalKernelFuncLabel = kernelLabel
+ self.cpuElementComputationBlock = block
+ self.gradComputationBlock = gradBlock
+ self.inputTensors = inputTensors
+ self.outputTensors = outputTensors
+ }
+
+ /// Convenience initializer
+ /// Subclass should override this function to assign `cpuComputeBlock` and `metalKernelFuncLabel`
+ ///
+ /// - Parameter computationDelegate: computationDelegate
+ public convenience required init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (inputTensor: Tensor, oututTensor: Tensor) -> Void in
+ fatalError()
+ }
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ fatalError()
+ }
+ let defaultLabel = "NEED OVERRIDE"
+ let kernelLabel = "NEED OVERRIDE"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+
+
+ /// Initial by assign input and output tensors
+ ///
+ /// - Parameters:
+ /// - inputTensors: inputTensors description
+ /// - outputTensors: outputTensors description
+ public convenience init(inputTensors:[Tensor], outputTensors:[Tensor]) {
+ self.init(computationDelegate: nil)
+ self.inputTensors = inputTensors
+ self.outputTensors = outputTensors
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+ /// This operator would not do anything about shapes.
+ /// Basically, it just return input shapes identically.
+ ///
+ /// - Note: If the `shapeArray` is empty, function returns `nil`.
+ ///
+ /// - Parameter shapes: input shapes
+ /// - Returns: return shapes
+ public func outputShape(shapeArray shapes: [TensorShape]) -> [TensorShape]? {
+ guard shapes.count != 0 else {
+ SerranoLogging.warningLogging(message: "Input shapes are empty", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+ return shapes
+ }
+
+ /// The `inputTensors` and `outputTensors` should be have same count of tensors and each tensor should be has same dimension.
+ ///
+ ///
+ public func inputOutputTensorsCheck() -> (check: Bool, msg: String) {
+ // input not nil
+ guard self.inputTensors != nil else {
+ return (false, "Input tensors are nil.")
+ }
+
+ // output not nil
+ guard self.outputTensors != nil else {
+ return (false, "Output tensors are nil.")
+ }
+
+ // same count
+ guard self.outputTensors!.count == self.inputTensors!.count else {
+ return (false, "Input tensors count is \(self.inputTensors!.count). " +
+ "Output tensors count is \(self.outputTensors!.count)." +
+ "Should be equal.")
+ }
+
+ // input shape check
+ let inputShapes = self.inputTensors!.map { $0.shape }
+ let outputShapeCheck = self.outputShape(shapeArray: inputShapes)
+ guard outputShapeCheck != nil else {
+ return (false, "Input tensors shapes are invalid. Check log for details.")
+ }
+
+ // output shape check
+ let outputShapes = self.outputTensors!.map { $0.shape }
+ for (i, t) in zip(outputShapes, outputShapeCheck!).enumerated() {
+ guard t.0 == t.1 else {
+ return (false, "Expect output tensor shape \(t.1.description), given \(t.0.description) at index \(i)")
+ }
+ }
+
+ return (true, "")
+ }
+
+ /// Compute asynclly
+ ///
+ /// - Parameters:
+ /// - tensors: input tensors
+ /// - computationMode: computation mode
+ public func computeAsync(_ computationMode: OperatorComputationMode = SerranoEngine.configuredEngine.defaultComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.compute(computationMode)
+ }
+ }
+
+ /// Compute synclly.
+ ///
+ /// - Parameters:
+ /// - tensors: input tensors
+ /// - computationMode: cmputation mode. If choose `GPU` but haven't configued a GPU SerranoEngine, operator will use `CPU` to compute.
+ /// - Returns: result tensors
+ public func compute(_ computationMode: OperatorComputationMode = SerranoEngine.configuredEngine.defaultComputationMode) {
+ // check
+ if !self.disableInputOutputCheck {
+ let (pass, msg) = self.inputOutputTensorsCheck()
+ guard pass else {
+ SerranoLogging.errorLogging(message: "Operator \(self.operatorLabel) calculation aborted cause invalid input tensors or output tensors: \(msg)", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ }
+
+ self.computationDelegate?.operatorWillBeginComputation(self)
+
+ switch computationMode {
+ case .GPU:
+ if !SerranoEngine.configuredEngine.hasAvailableGPU() {
+ SerranoLogging.warningLogging(message: "Serrano Engine has no available configured GPU device. Use CPU doing calculation instead.", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ self.cpu()
+ } else {
+ self.gpu()
+ }
+ case .CPU:
+ self.cpu()
+ case .Auto:
+ // TODO: More intelligent way to decide
+ if self.inputTensors![0].count > 1000000 && SerranoEngine.configuredEngine.hasAvailableGPU(){
+ self.gpu()
+ } else {
+ self.cpu()
+ }
+ }
+ self.computationDelegate?.operatorDidEndComputation(self, outputTensors: self.outputTensors!)
+ }
+
+ /// Calulate grads sync.
+ /// All unary operator return grads tensor with same number and shape as attribute `inputTensors`.
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ /// - Returns: return grads tensor
+ public func gradCompute(_ computationMode: OperatorComputationMode) -> [String: DataSymbolSupportedDataType] {
+ let grads = self.gradComputationBlock(self.inputTensors!, computationMode)
+ var result = [String: DataSymbolSupportedDataType]()
+ for (i, grad) in grads.enumerated() {
+ result["input_\(i)"] = grad
+ }
+ return result
+ }
+
+ /// Cal grads async
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ public func gradComputAsync(_ computationMode: OperatorComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.computationDelegate?.operatorWillBeginGradsComputation(self)
+ let result = self.gradCompute(computationMode)
+ self.computationDelegate?.operatorDidEndGradsComputation(self, grads: result)
+ }
+ }
+
+// /// An unary operator's representation graph just has inputs tensor symbols, output tensor symbols
+// /// and an operator symbol.
+// ///
+// /// - Returns: graph object.
+// public func addedToGraph(with InputSymbols: [TensorSymbol]) -> Graph {
+// let graph = ComputationGraph()
+//
+// let outputSymbols =
+//
+// return graph
+// }
+
+ /// This operator has no parameters. Do nothing
+ ///
+ public func bindParamSymbols(_ symbols: [GraphSymbol]) {
+
+ }
+
+ /// This operator has no parameters.
+ ///
+ /// - Returns: An empty array
+ public func paramSymbols() -> [GraphSymbol] {
+ return [GraphSymbol]()
+ }
+
+ /// Use cpu do the inplace computation.
+ /// Default, `UnaryOperator` defines a workflow. Subclass just needs to override `cpuElementComputationBlock`.
+ /// If subclass needs custom flow, it could just override this function.
+ ///
+ /// - Parameter tensors: the operation tensors
+ internal func cpu() {
+ let workGroup = DispatchGroup()
+ for tensorIndex in 0...size)
+ guard countBuffer != nil else {
+ fatalError("[Serrano] Failed to careate MTLBuffer.")
+ }
+ SerranoLogging.stdLogging(message: "Allocated a Metal buffer [\(countBuffer!.length) bytes] requested for count info \(count) by operator \(self.operatorLabel)", file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+
+ // encoder
+ let encoder = commandBuffer!.makeComputeCommandEncoder()
+ encoder.setComputePipelineState(kernel!)
+ encoder.setBuffer(inputBuffers[bufferIndex].buffer, offset: inputBuffers[bufferIndex].offset, at: 0)
+ encoder.setBuffer(resultBuffers[bufferIndex].buffer, offset: resultBuffers[bufferIndex].offset, at: 1)
+ encoder.setBuffer(countBuffer, offset: 0, at: 2)
+
+ // dispatch
+ let threadsPerThreadgroup = MTLSizeMake(kernel!.threadExecutionWidth,
+ 1,
+ 1)
+ let threadgroupsPerGrid = MTLSizeMake((self.inputTensors![bufferIndex].count + threadsPerThreadgroup.width - 1) / threadsPerThreadgroup.width,
+ 1,
+ 1)
+ encoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup)
+ SerranoLogging.stdLogging(message: "Dispatch group configured with threadgroupsPerGrid: \(threadgroupsPerGrid), threadsPerThreadgroup: \(threadsPerThreadgroup) requested by operator \(self.operatorLabel)", file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+ encoder.endEncoding()
+ }
+
+ // commit command buffer
+ commandBuffer!.commit()
+ commandBuffer!.waitUntilCompleted()
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+/**
+Compute element-wise sine on input tensors.
+
+- Note: This operator may output `NaN` or `Infinite` values and operator would not check these situations.
+ */
+public class SinOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvsinf(outputAddress, inputAddress, &count)
+ }
+
+ // cos(x)
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors(inputs.map{$0.shape})
+ let cosOp = CosOperator(inputTensors: inputs, outputTensors: grads)
+ cosOp.disableInputOutputCheck = true
+ cosOp.compute(mode)
+ return grads
+ }
+
+ let defaultLabel = "SinOperator"
+ let kernelLabel = "Sin"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise tangent on input tensors.
+
+- Note: This operator may output `NaN` or `Infinite` values and operator would not check these situations.
+ */
+public class TanOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvtanf(outputAddress, inputAddress, &count)
+ }
+
+ // 1 / cos(x)^2
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors(inputs.map{$0.shape})
+ let cosOp = CosOperator(inputTensors: inputs, outputTensors: grads)
+ cosOp.disableInputOutputCheck = true
+ cosOp.compute(mode)
+ for grad in grads {
+ grad &* grad
+ 1.0 &/ grad
+ }
+ return grads
+ }
+
+ let defaultLabel = "TanOperator"
+ let kernelLabel = "Tan"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise cosine on input tensors.
+
+- Note: This operator may output `NaN` or `Infinite` values and operator would not check these situations.
+ */
+public class CosOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvcosf(outputAddress, inputAddress, &count)
+ }
+
+ // sin(x)
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors(inputs.map{$0.shape})
+ let sinOp = SinOperator(inputTensors: inputs, outputTensors: grads)
+ sinOp.disableInputOutputCheck = true
+ sinOp.compute(mode)
+ return grads
+ }
+
+ let defaultLabel = "CosOperator"
+ let kernelLabel = "Cos"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise arc sine on input tensors.
+
+- Note: This operator may output `NaN` or `Infinite` values and operator would not check these situations.
+ */
+public class ArcsinOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvasinf(outputAddress, inputAddress, &count)
+ }
+
+ // 1 / sqrt(1-x^2)
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors(inputs.map{$0.shape})
+ let copyOp = CopyOperator(inputTensors: inputs, outputTensors: grads)
+ copyOp.disableInputOutputCheck = true
+ copyOp.compute(mode)
+ for grad in grads {
+ 1.0 &- (grad &* grad)
+ let sqrtOp = SqrtOperator(inputTensors: [grad], outputTensors: [grad])
+ sqrtOp.disableInputOutputCheck = true
+ sqrtOp.compute(mode)
+ 1 &/ grad
+ }
+ return grads
+ }
+ let defaultLabel = "ArcsinOperator"
+ let kernelLabel = "Arcsin"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise arc cosine on input tensors.
+
+- Note: This operator may output `NaN` or `Infinite` values and operator would not check these situations.
+ */
+public class ArccosOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvacosf(outputAddress, inputAddress, &count)
+ }
+
+ // -1/sqrt(1-x^2)
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors(inputs.map{$0.shape})
+ let copyOp = CopyOperator(inputTensors: inputs, outputTensors: grads)
+ copyOp.disableInputOutputCheck = true
+ copyOp.compute(mode)
+ for grad in grads {
+ 1.0 &- (grad &* grad)
+ let sqrtOp = SqrtOperator(inputTensors: [grad], outputTensors: [grad])
+ sqrtOp.disableInputOutputCheck = true
+ sqrtOp.compute(mode)
+ -1.0 &/ grad
+ }
+ return grads
+ }
+
+ let defaultLabel = "ArccosOperator"
+ let kernelLabel = "Arccos"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise arc tangent on input tensors.
+
+- Note: This operator may output `NaN` or `Infinite` values and operator would not check these situations.
+ */
+public class ArctanOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvatanf(outputAddress, inputAddress, &count)
+ }
+
+ // 1 / (1 + x^2)
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ // First allocate as managed to speed up incase using GPU with reusing MTLBuffers
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors(inputs.map{$0.shape})
+
+ // copy
+ let copyOp = CopyOperator(inputTensors: inputs, outputTensors: grads)
+ copyOp.disableInputOutputCheck = true
+ copyOp.compute(mode)
+
+ for grad in grads {
+ 1.0 &/ (1.0 &+ (grad &* grad))
+ }
+
+
+ return grads
+ }
+
+ let defaultLabel = "ArctanOperator"
+ let kernelLabel = "Arctan"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise of input tensors radians to degrees.
+ */
+public class DegreeOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ let count = vDSP_Length(output.count)
+ var convert:Float = 180.0 / 3.1415926
+ vDSP_vsmul(inputAddress, 1, &convert, outputAddress, 1, count)
+ }
+
+ // 180.0 / 3.1415926
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ var grads = [Tensor]()
+ let val:Float = 180.0 / 3.1415926
+ for input in inputs {
+ grads.append(Tensor(repeatingValue: val, tensorShape: input.shape))
+ }
+
+
+ return grads
+ }
+
+ let defaultLabel = "DegreeOperator"
+ let kernelLabel = "Degree"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise abs values of input tensors.
+ */
+public class AbsOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ let count = UInt(output.count)
+ vDSP_vabs(inputAddress, 1, outputAddress, 1, count)
+ }
+
+ // x / |x|. Note x != 0. or calcualted value is NaN.
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors(inputs.map{$0.shape})
+
+ // abs
+ let absOp = AbsOperator(inputTensors: inputs, outputTensors: grads)
+ absOp.disableInputOutputCheck = true
+ absOp.compute(mode)
+
+ // div
+ for (input, grad) in zip(inputs, grads) {
+ let rdivOp = DivOperator(inputTensors: [input, grad], outputTensors: [grad])
+ rdivOp.disableInputOutputCheck = true
+ rdivOp.compute(mode)
+ }
+
+
+ return grads
+ }
+
+ let defaultLabel = "AbsOperator"
+ let kernelLabel = "Abs"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise radien values of input tensors fro degrees.
+ */
+public class RadianOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ let count = vDSP_Length(output.count)
+ var convert:Float = 3.1415926 / 180.0
+ vDSP_vsmul(inputAddress, 1, &convert, outputAddress, 1, count)
+ }
+
+ // 3.1415926 / 180.0
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ var grads = [Tensor]()
+ let val:Float = 3.1415926 / 180.0
+ for input in inputs {
+ grads.append(Tensor(repeatingValue: val, tensorShape: input.shape))
+ }
+
+
+ return grads
+ }
+
+ let defaultLabel = "RadienOperator"
+ let kernelLabel = "Radien"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise hyperbolic sine of input tensors.
+
+- Note: This operator may output `NaN` or `Infinite` values and operator would not check these situations.
+ */
+public class SinhOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvsinhf(outputAddress, inputAddress, &count)
+ }
+
+ // (e^x + e^-x) / 2
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors(inputs.map{$0.shape})
+
+ // e^x
+ let expOp = ExpOperator(inputTensors: inputs, outputTensors: grads)
+ expOp.disableInputOutputCheck = true
+ expOp.compute(mode)
+
+ // e^-x
+ var eNegative = [Tensor]()
+ for input in inputs { eNegative.append(-1 * input) }
+ expOp.inputTensors = eNegative
+ expOp.outputTensors = eNegative
+ expOp.compute(mode)
+
+ for (grad, negtivate) in zip(grads, eNegative) {
+ (grad &+ negtivate) &/ 2
+ }
+
+
+ return grads
+ }
+
+ let defaultLabel = "SinhOperator"
+ let kernelLabel = "Sinh"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise hyperbolic cosine of input tensors.
+
+- Note: This operator may output `NaN` or `Infinite` values and operator would not check these situations.
+ */
+public class CoshOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvcoshf(outputAddress, inputAddress, &count)
+ }
+
+ // (e^x - e^-x) / 2
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors(inputs.map{$0.shape})
+
+ // e^x
+ let expOp = ExpOperator(inputTensors: inputs, outputTensors: grads)
+ expOp.disableInputOutputCheck = true
+ expOp.compute(mode)
+
+ // e^-x
+ var eNegative = [Tensor]()
+ for input in inputs { eNegative.append(-1 * input) }
+ expOp.inputTensors = eNegative
+ expOp.outputTensors = eNegative
+ expOp.compute(mode)
+
+ for (grad, negtivate) in zip(grads, eNegative) {
+ (grad &- negtivate) &/ 2
+ }
+
+
+ return grads
+ }
+
+ let defaultLabel = "CoshOperator"
+ let kernelLabel = "Cosh"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise hyperbolic tangent of input tensors.
+
+- Note: This operator may output `NaN` or `Infinite` values and operator would not check these situations.
+ */
+public class TanhOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvtanhf(outputAddress, inputAddress, &count)
+ }
+
+ // 1 / cosh(x) ^ 2
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors(inputs.map{$0.shape})
+
+ let coshOp = CoshOperator(inputTensors: inputs, outputTensors: grads)
+ coshOp.disableInputOutputCheck = true
+ coshOp.compute(mode)
+
+ for grad in grads { 1.0 &/ (grad &* grad) }
+
+
+ return grads
+ }
+
+ let defaultLabel = "TanhOperator"
+ let kernelLabel = "Tanh"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise inverse hyperbolic tangent on input tensors.
+
+- Note: This operator may output `NaN` or `Infinite` values and operator would not check these situations.
+ */
+public class ArctanhOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvatanhf(outputAddress, inputAddress, &count)
+ }
+
+ // 1 / (1 - x^2)
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors(inputs.map{$0.shape})
+
+ let squareOp = SquareOperator(inputTensors: inputs, outputTensors: grads)
+ squareOp.disableInputOutputCheck = true
+ squareOp.compute(mode)
+
+ for grad in grads { 1.0 &/ (1.0 &- grad) }
+
+
+
+ // release from management pool
+ DispatchQueue.global(qos: .userInitiated).async { SerranoResourceManager.globalManager.releaseTensors(grads) }
+ return grads
+ }
+
+ let defaultLabel = "ArctanhOperator"
+ let kernelLabel = "Arctanh"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise inverse hyperbolic cosine on input tensors.
+
+- Note: This operator may output `NaN` or `Infinite` values and operator would not check these situations.
+ */
+public class ArccoshOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvacoshf(outputAddress, inputAddress, &count)
+ }
+
+ // 1 / sqrt(x^2 - 1)
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors(inputs.map{$0.shape})
+
+ // square
+ let squreOp = SquareOperator(inputTensors: inputs, outputTensors: grads)
+ squreOp.disableInputOutputCheck = true
+ squreOp.compute(mode)
+
+ for grad in grads { grad &- 1.0 }
+ let sqrtOp = SqrtOperator(inputTensors: inputs, outputTensors: grads)
+ sqrtOp.disableInputOutputCheck = true
+ sqrtOp.compute(mode)
+
+ for grad in grads { 1.0 &/ grad }
+
+
+ return grads
+ }
+
+ let defaultLabel = "ArccoshOperator"
+ let kernelLabel = "Arccosh"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise inverse hyperbolic cosine on input tensors.
+
+- Note: This operator may output `NaN` or `Infinite` values and operator would not check these situations.
+ */
+public class ArcsinhOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvasinhf(outputAddress, inputAddress, &count)
+ }
+
+ // 1 / sqrt(x^2 +1)
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors(inputs.map{$0.shape})
+
+ // square
+ let squreOp = SquareOperator(inputTensors: inputs, outputTensors: grads)
+ squreOp.disableInputOutputCheck = true
+ squreOp.compute(mode)
+
+ for grad in grads { grad &+ 1.0 }
+ let sqrtOp = SqrtOperator(inputTensors: inputs, outputTensors: grads)
+ sqrtOp.disableInputOutputCheck = true
+ sqrtOp.compute(mode)
+
+ for grad in grads { 1.0 &/ grad }
+
+
+ return grads
+ }
+
+ let defaultLabel = "ArcsinhOperator"
+ let kernelLabel = "Arcsinh"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Operator computes element-wise floor of the input tensors and returen result tensors.
+ */
+public class FloorOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvfloorf(outputAddress, inputAddress, &count)
+ }
+
+ // 0 for any inputs.
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ var grads = [Tensor]()
+ // 0
+ for input in inputs { grads.append(Tensor(repeatingValue: 0.0, tensorShape: input.shape)) }
+
+
+ return grads
+ }
+
+ let defaultLabel = "FloorOperator"
+ let kernelLabel = "Floor"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Operator computes element-wise floor of the input tensors and returen result tensors.
+ */
+public class CeilOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvceilf(outputAddress, inputAddress, &count)
+ }
+
+ // 0 for any inputs.
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ var grads = [Tensor]()
+ // 0
+ for input in inputs { grads.append(Tensor(repeatingValue: 0.0, tensorShape: input.shape)) }
+
+
+ return grads
+ }
+
+ let defaultLabel = "CeilOperator"
+ let kernelLabel = "Ceil"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Operator computes element-wise rounded value to the nearest integer of the input.
+ */
+public class RintOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvnintf(outputAddress, inputAddress, &count)
+ }
+
+ // 0 for any inputs.
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ var grads = [Tensor]()
+ // 0
+ for input in inputs { grads.append(Tensor(repeatingValue: 0.0, tensorShape: input.shape)) }
+
+
+ return grads
+ }
+
+ let defaultLabel = "RintOperator"
+ let kernelLabel = "Rint"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Operator computes element-wise rounded value to the truncating integers of input values.
+ */
+public class RoundOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvintf(outputAddress, inputAddress, &count)
+ }
+
+ // 0 for any inputs.
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ var grads = [Tensor]()
+ // 0
+ for input in inputs { grads.append(Tensor(repeatingValue: 0.0, tensorShape: input.shape)) }
+
+
+ return grads
+ }
+
+ let defaultLabel = "RoundOperator"
+ let kernelLabel = "Round"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise square values of input tensors.
+
+- Note: This operator may output `NaN` or `Infinite` values and operator would not check these situations.
+ */
+public class SquareOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ let count = UInt(output.count)
+ vDSP_vsq(inputAddress, 1, outputAddress, 1, count)
+ }
+
+ // 2x
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ var grads = [Tensor]()
+ for input in inputs { grads.append( 2.0 * input ) }
+
+
+ return grads
+ }
+
+ let defaultLabel = "SquareOperator"
+ let kernelLabel = "Square"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+Compute element-wise reciprocal values of square-root of input tensors.
+`1 / sqrt(x)`
+ */
+public class RsqrtOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvrsqrtf(outputAddress, inputAddress, &count)
+ }
+
+ // -0.5 * x^(-1.5)
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors(inputs.map{$0.shape})
+
+ let powOp = PowOperator()
+ powOp.disableInputOutputCheck = true
+ for (grad, input) in zip(grads, inputs) {
+ let const = Tensor(repeatingValue: -1.5, tensorShape: input.shape)
+ powOp.inputTensors = [input, const]
+ powOp.outputTensors = [grad]
+ powOp.compute(mode)
+ -0.5 &* grad
+ }
+
+
+ return grads
+ }
+
+ let defaultLabel = "RsqrtOperator"
+ let kernelLabel = "Rsqrt"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise square-root values of input tensors.
+ */
+public class SqrtOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvsqrtf(outputAddress, inputAddress, &count)
+ }
+
+ // 0.5 * x^(-0.5)
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors(inputs.map{$0.shape})
+
+ let powOp = PowOperator()
+ powOp.disableInputOutputCheck = true
+ for (grad, input) in zip(grads, inputs) {
+ let const = Tensor(repeatingValue: -0.5, tensorShape: input.shape)
+ powOp.inputTensors = [input, const]
+ powOp.outputTensors = [grad]
+ powOp.compute(mode)
+ 0.5 &* grad
+ }
+
+
+ return grads
+ }
+
+ let defaultLabel = "SqrtOperator"
+ let kernelLabel = "Sqrt"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+Compute element-wise `log(1 + x)` values of input tensors.
+Base is `e`.
+ */
+public class Log1pOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvlog1pf(outputAddress, inputAddress, &count)
+ }
+
+ // 1 / (ln(10) * (1+x))
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ var grads = [Tensor]()
+ let ln10: Float = log(10.0)
+ for input in inputs {
+ grads.append(1.0 &/ (ln10 &* (1.0 + input)))
+ }
+
+
+ return grads
+ }
+
+ let defaultLabel = "Log1pOperator"
+ let kernelLabel = "Log1p"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise `log2(x)` values of input tensors.
+ */
+public class Log2Operator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvlog2f(outputAddress, inputAddress, &count)
+ }
+
+ // 1 / (ln(2) * x)
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ var grads = [Tensor]()
+ let ln2: Float = log(2.0)
+ for input in inputs {
+ grads.append(1.0 &/ (ln2 * input))
+ }
+
+
+ return grads
+ }
+
+ let defaultLabel = "Log2Operator"
+ let kernelLabel = "Log2"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+
+/**
+ Compute element-wise `log10(x)` values of input tensors.
+ */
+public class Log10Operator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvlog10f(outputAddress, inputAddress, &count)
+ }
+
+ // 1 / (ln(10) * x)
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ var grads = [Tensor]()
+ let ln10: Float = log(10.0)
+ for input in inputs {
+ grads.append(1.0 &/ (ln10 * input))
+ }
+
+
+ return grads
+ }
+
+ let defaultLabel = "Log10Operator"
+ let kernelLabel = "Log10"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+Compute element-wise `log(x)` values of input tensors.
+Base is `e`.
+ */
+public class LogOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvlogf(outputAddress, inputAddress, &count)
+ }
+
+ // 1 / x
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ var grads = [Tensor]()
+ for input in inputs {
+ grads.append(1.0 / input)
+ }
+
+
+ return grads
+ }
+
+ let defaultLabel = "LogOperator"
+ let kernelLabel = "Log"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+ Compute element-wise `exp(x) - 1` values of input tensors.
+
+- Note: This operator may output `NaN` or `Infinite` values and operator would not check these situations.
+ */
+public class Expm1Operator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvexpm1f(outputAddress, inputAddress, &count)
+ }
+
+ // e^x
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors(inputs.map{$0.shape})
+ let expOp = ExpOperator(inputTensors: inputs, outputTensors: grads)
+ expOp.disableInputOutputCheck = true
+ expOp.compute(mode)
+
+ return grads
+ }
+
+ let defaultLabel = "Expm1Operator"
+ let kernelLabel = "Expm1"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+
+/**
+ Compute element-wise `exp(x)` values of input tensors.
+
+- Note: This operator may output `NaN` or `Infinite` values and operator would not check these situations.
+ */
+public class ExpOperator: UnaryOperator {
+
+ /// Override init
+ ///
+ /// - Parameter computationDelegate: delegate
+ public required convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (input: Tensor, output: Tensor) -> Void in
+ let inputAddress = input.contentsAddress
+ let outputAddress = output.contentsAddress
+ var count = Int32(output.count)
+ vvexpf(outputAddress, inputAddress, &count)
+ }
+
+ // e^x
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ let grads = SerranoResourceManager.globalManager.allocateUnamangedTensors(inputs.map{$0.shape})
+ let expOp = ExpOperator(inputTensors: inputs, outputTensors: grads)
+ expOp.disableInputOutputCheck = true
+ expOp.compute(mode)
+ return grads
+ }
+
+ let defaultLabel = "ExpOperator"
+ let kernelLabel = "Exp"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
diff --git a/Source/Serrano/operators/nn/activation/activation_op.metal b/Source/Serrano/operators/nn/activation/activation_op.metal
new file mode 100644
index 0000000..97bdc2d
--- /dev/null
+++ b/Source/Serrano/operators/nn/activation/activation_op.metal
@@ -0,0 +1,100 @@
+//
+// activation_op.metal
+// serrano
+//
+// Created by ZHONGHAO LIU on 6/26/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+#include
+
+using namespace metal;
+
+namespace serrano_ios {
+ kernel void ReLU(constant float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint& count [[ buffer(2) ]],
+ constant float& alpha [[buffer(3)]],
+ uint2 gid [[ thread_position_in_grid ]]) {
+ if (gid.x >= count) return;
+ out_tensor[gid.x] = max(alpha, in_tensor[gid.x]);
+ }
+
+ kernel void Sigmoid(constant float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint& count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]]) {
+ if (gid.x >= count) return;
+ out_tensor[gid.x] = 1.0f / (1.0f + exp(-in_tensor[gid.x]));
+ }
+
+ kernel void Softplus(constant float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint& count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]]) {
+ if (gid.x >= count) return;
+ out_tensor[gid.x] = log(exp(in_tensor[gid.x]) + 1.0f);
+ }
+
+ kernel void Softsign(constant float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint& count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]]) {
+ if (gid.x >= count) return;
+ out_tensor[gid.x] = in_tensor[gid.x] / ( 1.0f + abs(in_tensor[gid.x]));
+ }
+
+ kernel void Linear(constant float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint& count [[ buffer(2) ]],
+ uint2 gid [[ thread_position_in_grid ]]) {
+ if (gid.x >= count) return;
+ out_tensor[gid.x] = in_tensor[gid.x];
+ }
+
+ kernel void ELU(constant float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint& count [[ buffer(2) ]],
+ constant float& alpha [[buffer(3)]],
+ uint2 gid [[ thread_position_in_grid ]]) {
+ if (gid.x >= count) return;
+ out_tensor[gid.x] = (in_tensor[gid.x] >= 0.0f ? in_tensor[gid.x] : alpha*(exp(in_tensor[gid.x]) - 1.0f));
+ }
+
+ kernel void SELU(constant float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint& count [[ buffer(2) ]],
+ constant float& alpha [[buffer(3)]],
+ constant float& scale [[buffer(4)]],
+ uint2 gid [[ thread_position_in_grid ]]) {
+ if (gid.x >= count) return;
+ out_tensor[gid.x] = (in_tensor[gid.x] >= 0.0f ? scale * in_tensor[gid.x] : scale * alpha * (exp(in_tensor[gid.x]) - 1.0f));
+ }
+
+ kernel void LeakyReLU(constant float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint& count [[ buffer(2) ]],
+ constant float& alpha [[buffer(3)]],
+ uint2 gid [[ thread_position_in_grid ]]) {
+ if (gid.x >= count) return;
+ out_tensor[gid.x] = (in_tensor[gid.x] >= 0.0f ? in_tensor[gid.x] : alpha * in_tensor[gid.x]);
+ }
+
+ kernel void ThresholdedReLU(constant float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint& count [[ buffer(2) ]],
+ constant float& alpha [[buffer(3)]],
+ uint2 gid [[ thread_position_in_grid ]]) {
+ if (gid.x >= count) return;
+ out_tensor[gid.x] = (in_tensor[gid.x] > alpha ? in_tensor[gid.x] : 0.0f);
+ }
+
+ kernel void PReLU(constant float* in_tensor [[ buffer(0) ]],
+ device float* out_tensor [[ buffer(1) ]],
+ constant uint& count [[ buffer(2) ]],
+ constant float* alpha [[buffer(3)]],
+ uint2 gid [[ thread_position_in_grid ]]) {
+ if (gid.x >= count) return;
+ out_tensor[gid.x] = (in_tensor[gid.x] >= 0.0f ? in_tensor[gid.x] : in_tensor[gid.x] * alpha[gid.x]);
+ }
+}
diff --git a/Source/Serrano/operators/nn/activation/activation_op.swift b/Source/Serrano/operators/nn/activation/activation_op.swift
new file mode 100644
index 0000000..8cecbc1
--- /dev/null
+++ b/Source/Serrano/operators/nn/activation/activation_op.swift
@@ -0,0 +1,1247 @@
+//
+// activation_op.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 6/26/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import Metal
+import Dispatch
+import Accelerate
+
+/**
+The abstract class for all activation operator.
+An activation operator is actually a special kind of `UnaryOperator` doing a little bit more complex element-wise computation.
+*/
+public class ActivationOperator: UnaryOperator {
+
+ /// Convenience initializer
+ /// Subclass required to override this function to assign `cpuComputeBlock` and `metalKernelFuncLabel`
+ ///
+ /// - Parameter computationDelegate: computationDelegate
+ required public convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (inputTensor: Tensor, oututTensor: Tensor) -> Void in
+ fatalError("Not implemented")
+ }
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ fatalError("Not implemented")
+ }
+ let defaultLabel = "NEED OVERRIDE"
+ let kernelLabel = "NEED OVERRIDE"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+
+ /// Construct an `ActivationOperator` from a `String` name.
+ public convenience init(activationOpName: String) {
+ fatalError()
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+Rectified Linear Unit,
+`y=max(x,alpha=0.0)`
+*/
+public class ReLUOperator: ActivationOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// hyperparameter, default is `0.0`
+ public var alpha: Float = 0.0
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+ /// Convenience initializer
+ /// Subclass required to override this function to assign `cpuComputeBlock` and `metalKernelFuncLabel`
+ ///
+ /// - Parameter computationDelegate: computationDelegate
+ required public convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (inputTensor: Tensor, oututTensor: Tensor) -> Void in
+ fatalError("Not implemented")
+ }
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ //TODO: implemented
+ fatalError("Not implemented")
+ }
+ let defaultLabel = "ReLUOperator"
+ let kernelLabel = "ReLU"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+
+ /// Initial by setting `alpha` value
+ ///
+ /// - Parameters:
+ /// - computationDelegate: computationDelegate description
+ /// - alpha: alpha description
+ public convenience init(computationDelegate: OperatorCalculationDelegate? = nil, alpha: Float) {
+ self.init(computationDelegate: computationDelegate)
+ self.alpha = alpha
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+ /// Attribute `alpha` as a `ScalarSymbol`.
+ ///
+ /// - Returns: Array of GraphSymbol
+ public override func paramSymbols() -> [GraphSymbol] {
+ let alpha = SerranoScalarSymbol("alpha", dataType: .float, dataSource: .Default)
+ alpha.bindedData = Float(0.0)
+ return [alpha]
+ }
+
+ /// Override CPU
+ internal override func cpu() {
+ let workGroup = DispatchGroup()
+ for tensorIndex in 0...size)
+ guard countBuffer != nil else {
+ SerranoLogging.errorLogging(message: " Failed to careate MTLBuffer.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ SerranoLogging.stdLogging(message: "Allocated a Metal buffer [\(countBuffer!.length) bytes] requested for count info \(count) by operator \(self.operatorLabel)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+ countBuffers.append(countBuffer!)
+
+ }
+ resourcePrepareGroup.leave()
+ }
+
+ // alpha buffer
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ alphaBuffer = engine.GPUDevice?.makeBuffer(bytes: &self.alpha,
+ length: MemoryLayout.size)
+ guard alphaBuffer != nil else {
+ SerranoLogging.errorLogging(message: " Failed to careate MTLBuffer.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ SerranoLogging.stdLogging(message: "Allocated a Metal buffer [\(alphaBuffer!.length) bytes] requested for alpha value \(self.alpha) by operator \(self.operatorLabel)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+ resourcePrepareGroup.leave()
+ }
+ resourcePrepareGroup.wait()
+
+ for bufferIndex in 0.. Void in
+ let inputPointer = inputTensor.contentsAddress
+ let outputPointer = outputTensor.contentsAddress
+ let oneValueTensor = Tensor(repeatingValue: 1.0, tensorShape: outputTensor.shape)
+ let oneValuePointer = oneValueTensor.contentsAddress
+ var count32 = Int32(outputTensor.count)
+ let count = vDSP_Length(outputTensor.count)
+ //copy value from input to output
+ cblas_scopy(count32, inputPointer, 1, outputPointer, 1)
+ // negative
+ vDSP_vneg(outputPointer, 1, outputPointer, 1, count)
+ // exp
+ vvexpf(outputPointer, outputPointer, &count32)
+ // plus
+ cblas_saxpy(count32, 1.0, oneValuePointer, 1, outputPointer, 1)
+ // reciprocal
+ vvrecf(outputPointer, outputPointer, &count32)
+ }
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ //TODO: implemented
+ fatalError("Not implemented")
+ }
+ let defaultLabel = "SigmoidOperator"
+ let kernelLabel = "Sigmoid"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+Softplus.`y = log(exp(x) + 1)`
+*/
+public class SoftplusOperator: ActivationOperator {
+ /// Convenience initializer
+ /// Subclass required to override this function to assign `cpuComputeBlock` and `metalKernelFuncLabel`
+ ///
+ /// - Parameter computationDelegate: computationDelegate
+ required public convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (inputTensor: Tensor, outputTensor: Tensor) -> Void in
+ let inAddress = inputTensor.contentsAddress
+ let outAddress = outputTensor.contentsAddress
+ let count = vDSP_Length(inputTensor.count)
+ var count32 = Int32(inputTensor.count)
+ var one: Float = 1.0
+ // exp
+ vvexpf(outAddress, inAddress, &count32)
+ // plus 1
+ vDSP_vsadd(outAddress, 1, &one, outAddress, 1, count)
+ // log
+ vvlogf(outAddress, outAddress, &count32)
+ }
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ //TODO: implemented
+ fatalError("Not implemented")
+ }
+ let defaultLabel = "SoftplusOperator"
+ let kernelLabel = "Softplus"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+
+/**
+Softsign.`y = x / (1 + abs(x))`
+*/
+public class SoftsignOperator: ActivationOperator {
+ /// Convenience initializer
+ /// Subclass required to override this function to assign `cpuComputeBlock` and `metalKernelFuncLabel`
+ ///
+ /// - Parameter computationDelegate: computationDelegate
+ required public convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (inputTensor: Tensor, outputTensor: Tensor) -> Void in
+ let inputAddress = inputTensor.contentsAddress
+ let outAddress = outputTensor.contentsAddress
+ let count = vDSP_Length(outputTensor.count)
+ var one:Float = 1.0
+ // abs
+ vDSP_vabs(inputAddress, 1, outAddress, 1, count)
+ // add
+ vDSP_vsadd(outAddress, 1, &one, outAddress, 1, count)
+ // div
+ vDSP_vdiv(outAddress, 1, inputAddress, 1, outAddress, 1, count)
+ }
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ //TODO: implemented
+ fatalError("Not implemented")
+ }
+ let defaultLabel = "SoftsignOperator"
+ let kernelLabel = "Softsign"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+
+/**
+Do nothing. Output identify tensor.
+`y = x`
+*/
+public class LinearOperator: ActivationOperator {
+ /// Convenience initializer
+ /// Subclass required to override this function to assign `cpuComputeBlock` and `metalKernelFuncLabel`
+ ///
+ /// - Parameter computationDelegate: computationDelegate
+ required public convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (inputTensor: Tensor, outputTensor: Tensor) -> Void in
+ let inputPointer = inputTensor.contentsAddress
+ let outputPointer = outputTensor.contentsAddress
+ let count32 = Int32(outputTensor.count)
+ cblas_scopy(count32, inputPointer, 1, outputPointer, 1)
+ }
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ //TODO: implemented
+ fatalError("Not implemented")
+ }
+ let defaultLabel = "LinearOperator"
+ let kernelLabel = "Linear"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+}
+
+/**
+Exponential Linear Units.
+
+`y = x if x > 0, else y = alpha*(exp(x) - 1)`
+
+## Reference
+[Fast and Accurate Deep Network Learning by Exponential Linear Units (ELUs)](https://arxiv.org/abs/1511.07289)
+*/
+public class ELUOperator: ActivationOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// hyperparameter, default is `1.0`
+ public var alpha:Float = 1.0
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializer
+
+ /// Convenience initializer
+ /// Subclass required to override this function to assign `cpuComputeBlock` and `metalKernelFuncLabel`
+ ///
+ /// - Parameter computationDelegate: computationDelegate
+ required public convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (inputTensor: Tensor, outputTensor: Tensor) -> Void in
+ print("NOT USE") // will override cpu(). this block will not be used
+ }
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ //TODO: implemented
+ fatalError("Not implemented")
+ }
+ let defaultLabel = "ELUOperator"
+ let kernelLabel = "ELU"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+
+
+ /// Initial by setting `alpha` value
+ ///
+ /// - Parameters:
+ /// - computationDelegate: computationDelegate description
+ /// - alpha: alpha description
+ public convenience init(computationDelegate: OperatorCalculationDelegate? = nil, alpha: Float) {
+ self.init(computationDelegate: computationDelegate)
+ self.alpha = alpha
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+ /// Attribute `alpha` as a `ScalarSymbol`.
+ ///
+ /// - Returns: Array of GraphSymbol
+ public override func paramSymbols() -> [GraphSymbol] {
+ let alpha = SerranoScalarSymbol("alpha", dataType: .float, dataSource: .Default)
+ alpha.bindedData = Float(1.0)
+ return [alpha]
+ }
+
+ /// Override CPU
+ internal override func cpu() {
+ let workGroup = DispatchGroup()
+ for tensorIndex in 0.. 0.0 { outputReadre[i] = inputReader[i] }
+ else { outputReadre[i] = self.alpha * (exp(inputReader[i]) - 1.0) }
+ }
+ workGroup.leave()
+ }
+ }
+
+ workGroup.wait()
+ }
+
+ /// Override GPU calculation cause there's a hyperparameter to pass-in
+ internal override func gpu() {
+ // prepare resource
+ let resourcePrepareGroup = DispatchGroup()
+ let engine = SerranoEngine.configuredEngine
+ var kernel: MTLComputePipelineState?
+ var commandBuffer: MTLCommandBuffer?
+ var inputBuffers: [MTLBufferResource] = [MTLBufferResource]()
+ var resultBuffers: [MTLBufferResource] = [MTLBufferResource]()
+ var countBuffers: [MTLBuffer] = [MTLBuffer]()
+ var alphaBuffer: MTLBuffer?
+
+ //// kernel
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ var info = ""
+ (kernel, info) = engine.loadGPUKernel(kernelLabel: self.metalKernelFuncLabel)
+ guard kernel != nil else {
+ fatalError("[Serrano] Failed to load kernel \(self.metalKernelFuncLabel). Info: \(info)")
+ }
+ resourcePrepareGroup.leave()
+ }
+
+ //// command buffer
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ commandBuffer = engine.serranoCommandQueue?.makeCommandBuffer()
+ guard commandBuffer != nil else {
+ fatalError("[Serrano] Failed to make new command buffer.")
+ }
+ resourcePrepareGroup.leave()
+ }
+
+ // prepare tensor MTLBuffers
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ inputBuffers = SerranoResourceManager.globalManager.allocateMTLBufferResources(self.inputTensors!)
+ resultBuffers = SerranoResourceManager.globalManager.allocateMTLBufferResources(self.outputTensors!)
+ resourcePrepareGroup.leave()
+ }
+
+ // count buffers
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ for tensor in self.outputTensors! {
+ var count = UInt32(tensor.count)
+ let countBuffer = engine.GPUDevice?.makeBuffer(bytes: &count,
+ length: MemoryLayout.size)
+ guard countBuffer != nil else {
+ SerranoLogging.errorLogging(message: " Failed to careate MTLBuffer.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ SerranoLogging.stdLogging(message: "Allocated a Metal buffer [\(countBuffer!.length) bytes] requested for count info \(count) by operator \(self.operatorLabel)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+ countBuffers.append(countBuffer!)
+
+ }
+ resourcePrepareGroup.leave()
+ }
+
+ // alpha buffer
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ alphaBuffer = engine.GPUDevice?.makeBuffer(bytes: &self.alpha,
+ length: MemoryLayout.size)
+ guard alphaBuffer != nil else {
+ SerranoLogging.errorLogging(message: " Failed to careate MTLBuffer.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ SerranoLogging.stdLogging(message: "Allocated a Metal buffer [\(alphaBuffer!.length) bytes] requested for alpha value \(self.alpha) by operator \(self.operatorLabel)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+ resourcePrepareGroup.leave()
+ }
+ resourcePrepareGroup.wait()
+
+
+
+ for bufferIndex in 0.. Void in
+ print("NOT USE")
+ }
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ //TODO: implemented
+ fatalError("Not implemented")
+ }
+ let defaultLabel = "SELUOperator"
+ let kernelLabel = "SELU"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+
+ /// Initial by setting `alpha` value and `scale` values.
+ ///
+ /// - Parameters:
+ /// - computationDelegate: computationDelegate description
+ /// - alpha: alpha description
+ public convenience init(computationDelegate: OperatorCalculationDelegate? = nil, alpha: Float, scale:Float) {
+ self.init(computationDelegate: computationDelegate)
+ self.alpha = alpha
+ self.scale = scale
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+ /// Attribute `alpha` as a `ScalarSymbol`.
+ /// Attribute `scale` as a `ScalarSymbol`
+ ///
+ /// - Returns: Array of GraphSymbol
+ public override func paramSymbols() -> [GraphSymbol] {
+ let alpha = SerranoScalarSymbol("alpha", dataType: .float, dataSource: .Default)
+ alpha.bindedData = Float(1.673263)
+
+ let scale = SerranoScalarSymbol("scale", dataType: .float, dataSource: .Default)
+ alpha.bindedData = Float(1.050701)
+
+ return [alpha, scale]
+ }
+
+ /// Override CPU
+ internal override func cpu() {
+ let workGroup = DispatchGroup()
+ for tensorIndex in 0.. 0.0 { outputReadre[i] = inputReader[i] }
+ else { outputReadre[i] = self.alpha * (exp(inputReader[i]) - 1.0) }
+ outputReadre[i] *= self.scale
+ }
+ workGroup.leave()
+ }
+ }
+
+ workGroup.wait()
+ }
+
+ /// Override GPU calculation cause there's a hyperparameter to pass-in
+ internal override func gpu() {
+ // prepare resource
+ let resourcePrepareGroup = DispatchGroup()
+ let engine = SerranoEngine.configuredEngine
+ var kernel: MTLComputePipelineState?
+ var commandBuffer: MTLCommandBuffer?
+ var inputBuffers: [MTLBufferResource] = [MTLBufferResource]()
+ var resultBuffers: [MTLBufferResource] = [MTLBufferResource]()
+ var countBuffers: [MTLBuffer] = [MTLBuffer]()
+ var alphaBuffer: MTLBuffer?
+ var scaleBuffer: MTLBuffer?
+
+ //// kernel
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ var info = ""
+ (kernel, info) = engine.loadGPUKernel(kernelLabel: self.metalKernelFuncLabel)
+ guard kernel != nil else {
+ fatalError("[Serrano] Failed to load kernel \(self.metalKernelFuncLabel). Info: \(info)")
+ }
+ resourcePrepareGroup.leave()
+ }
+
+ //// command buffer
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ commandBuffer = engine.serranoCommandQueue?.makeCommandBuffer()
+ guard commandBuffer != nil else {
+ fatalError("[Serrano] Failed to make new command buffer.")
+ }
+ resourcePrepareGroup.leave()
+ }
+
+ // prepare tensor MTLBuffers
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ inputBuffers = SerranoResourceManager.globalManager.allocateMTLBufferResources(self.inputTensors!)
+ resultBuffers = SerranoResourceManager.globalManager.allocateMTLBufferResources(self.outputTensors!)
+ resourcePrepareGroup.leave()
+ }
+
+ // count buffers
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ for tensor in self.outputTensors! {
+ var count = UInt32(tensor.count)
+ let countBuffer = engine.GPUDevice?.makeBuffer(bytes: &count,
+ length: MemoryLayout.size)
+ guard countBuffer != nil else {
+ SerranoLogging.errorLogging(message: " Failed to careate MTLBuffer.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ SerranoLogging.stdLogging(message: "Allocated a Metal buffer [\(countBuffer!.length) bytes] requested for count info \(count) by operator \(self.operatorLabel)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+ countBuffers.append(countBuffer!)
+
+ }
+ resourcePrepareGroup.leave()
+ }
+
+ // alpha buffer
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ alphaBuffer = engine.GPUDevice?.makeBuffer(bytes: &self.alpha,
+ length: MemoryLayout.size)
+ guard alphaBuffer != nil else {
+ SerranoLogging.errorLogging(message: " Failed to careate MTLBuffer.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ SerranoLogging.stdLogging(message: "Allocated a Metal buffer [\(alphaBuffer!.length) bytes] requested for alpha value \(self.alpha) by operator \(self.operatorLabel)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+ resourcePrepareGroup.leave()
+ }
+ resourcePrepareGroup.wait()
+
+ // scalue buffer
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ scaleBuffer = engine.GPUDevice?.makeBuffer(bytes: &self.scale,
+ length: MemoryLayout.size)
+ guard scaleBuffer != nil else {
+ SerranoLogging.errorLogging(message: " Failed to careate MTLBuffer.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ SerranoLogging.stdLogging(message: "Allocated a Metal buffer [\(scaleBuffer!.length) bytes] requested for alpha value \(self.scale) by operator \(self.operatorLabel)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+ resourcePrepareGroup.leave()
+ }
+ resourcePrepareGroup.wait()
+
+
+ for bufferIndex in 0..=0`.
+ /// Any negative value will be automatically making this attribute value to `-1`.
+ /// - Note: `-1` is a special value indicating last dim.
+ public var dim: Int = -1 {
+ didSet {
+ if self.dim < 0 { self.dim = -1 }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+ /// Convenience initializer
+ /// Subclass required to override this function to assign `cpuComputeBlock` and `metalKernelFuncLabel`
+ ///
+ /// - Parameter computationDelegate: computationDelegate
+ required public convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (inputTensor: Tensor, outputTensor: Tensor) -> Void in
+ print("NOT USE")
+ }
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ //TODO: implemented
+ fatalError("Not implemented")
+ }
+ let defaultLabel = "SoftmaxOperator"
+ let kernelLabel = ""
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ }
+
+ /// Initial by setting `dim` value.
+ ///
+ /// - Parameters:
+ /// - computationDelegate: computationDelegate description
+ /// - dim: reduce dim
+ public convenience init(computationDelegate: OperatorCalculationDelegate? = nil, dim: Int) {
+ self.init(computationDelegate: computationDelegate)
+ self.dim = dim
+ if self.dim < 0 { self.dim = -1 }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+ /// Override this function from `UnaryOperaotr` to do additional checking validation bewtween `dim` and `shapes`.
+ ///
+ /// - Parameter shapes: shapes description
+ /// - Returns: return value description
+ public override func outputShape(shapeArray shapes: [TensorShape]) -> [TensorShape]? {
+ // super check
+ let outShapes = super.outputShape(shapeArray: shapes)
+
+ // check dim and shapes
+ for shape in shapes {
+ guard self.dim < shape.shapeArray.count else {
+ SerranoLogging.errorLogging(message: "Invalid shape [\(shape.description)] for dim \(self.dim).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+ }
+ return outShapes
+ }
+
+ /// Override computaion methods.
+ /// This computation just call other operators.
+ ///
+ /// - Parameter computationMode: computationMode
+ public override func compute(_ computationMode: OperatorComputationMode = SerranoEngine.configuredEngine.defaultComputationMode) {
+ // check
+ let (pass, msg) = self.inputOutputTensorsCheck()
+ guard pass else {
+ SerranoLogging.errorLogging(message: "Operator \(self.operatorLabel) calculation aborted cause invalid input tensors or output tensors: \(msg)", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+
+
+ self.computationDelegate?.operatorWillBeginComputation(self)
+
+ let computeGroup = DispatchGroup()
+ for tensorIndex in 0.. [GraphSymbol] {
+ let dim = SerranoScalarSymbol("dim", dataType: .int, dataSource: .Default)
+ dim.bindedData = -1
+
+ return [dim]
+ }
+}
+
+/**
+LeakyReLU activation operator.
+```
+y = alpha * x (x < 0)
+y = x (x >= 0)
+```
+
+## scale factor alpha
+Default value is `0.3`
+
+## Reference
+[Rectifier Nonlinearities Improve Neural Network Acoustic Models](https://web.stanford.edu/~awni/papers/ReLU_hybrid_icml2013_final.pdf)
+*/
+public class LeakyReLUOperator: ELUOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializer
+
+ /// Convenience initializer
+ /// Subclass required to override this function to assign `cpuComputeBlock` and `metalKernelFuncLabel`
+ ///
+ /// - Parameter computationDelegate: computationDelegate
+ required public convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (inputTensor: Tensor, outputTensor: Tensor) -> Void in
+ print("NOT USE") // will override cpu(). this block will not be used
+ }
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ //TODO: implemented
+ fatalError("Not implemented")
+ }
+ let defaultLabel = "LeakyReLUOperator"
+ let kernelLabel = "LeakyReLU"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ self.alpha = 0.3
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+ /// Override CPU
+ internal override func cpu() {
+ let workGroup = DispatchGroup()
+ for tensorIndex in 0..= 0.0 { outputReadre[i] = inputReader[i] }
+ else { outputReadre[i] = self.alpha * inputReader[i] }
+ }
+ workGroup.leave()
+ }
+ }
+ workGroup.wait()
+ }
+
+}
+
+
+/**
+Thresholded Rectified Linear Unit.
+`f(x) = x if x > alpha, else f(x) = 0`
+
+## Threshold `alpha`
+Default value is `1.0`
+
+## Reference
+[Zero-Bias Autoencoders and the Benefits of Co-Adapting Features](https://arxiv.org/abs/1402.3337)
+*/
+public class ThresholdedReLUOperator: ELUOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializer
+
+ /// Convenience initializer
+ /// Subclass required to override this function to assign `cpuComputeBlock` and `metalKernelFuncLabel`
+ ///
+ /// - Parameter computationDelegate: computationDelegate
+ required public convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+ let block = { (inputTensor: Tensor, outputTensor: Tensor) -> Void in
+ print("NOT USE") // will override cpu(). this block will not be used
+ }
+ let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+ //TODO: implemented
+ fatalError("Not implemented")
+ }
+ let defaultLabel = "ThresholdedReLUOperator"
+ let kernelLabel = "ThresholdedReLU"
+ self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+ metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+ inputTensors: nil, outputTensors: nil)
+ self.alpha = 1.0
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+ /// Override CPU
+ internal override func cpu() {
+ let workGroup = DispatchGroup()
+ for tensorIndex in 0.. self.alpha { outputReadre[i] = inputReader[i] }
+ else { outputReadre[i] = 0.0 }
+ }
+ workGroup.leave()
+ }
+ }
+ workGroup.wait()
+ }
+}
+
+/**
+Parametric Rectified Linear Unit.
+
+```
+y_i = alpha_i * x_i (x_i< 0)
+y_i = x_i (x_i >= 0)
+```
+
+## Reference
+[Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification](https://arxiv.org/abs/1502.01852)
+*/
+//public class PReLUOperator: ActivationOperator {
+ //TODO: RE-IMPLEMENTATION
+
+// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// // MARK: - Attributes
+//
+//
+// /// A tensor
+// /// - Note: If this attribute is `nil` when doing calculation,
+// /// operator will use `defaultAlpha` as the default values.
+// public var alpha: Tensor
+//
+// /// Default alpha values when `alpha` is `nil`
+// public var defaultAlphaValue: Float = 0.3
+//
+// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// // MARK: - Initializers
+//
+// /// Convenience initializer
+// /// Subclass required to override this function to assign `cpuComputeBlock` and `metalKernelFuncLabel`
+// ///
+// /// - Parameter computationDelegate: computationDelegate
+// required public convenience init(computationDelegate: OperatorCalculationDelegate? = nil) {
+// let block = { (inputTensor: Tensor, outputTensor: Tensor) -> Void in
+// print("NOT USE") // will override cpu(). this block will not be used
+// }
+// let gradBlock = { (inputs: [Tensor], mode: OperatorComputationMode) -> [DataSymbolSupportedDataType] in
+// //TODO: implemented
+// fatalError("Not implemented")
+// }
+// let defaultLabel = "PReLUOperator"
+// let kernelLabel = "PReLU"
+// self.init(operatorLabel: defaultLabel, cpuComputeBlock: block, gradComputationBlock: gradBlock,
+// metalKernelFuncLabel: kernelLabel, computationDelegate: computationDelegate,
+// inputTensors: nil, outputTensors: nil)
+// self.alpha = nil
+// }
+//
+// /// Initial by setting `alpha` value
+// ///
+// /// - Parameters:
+// /// - computationDelegate: computationDelegate
+// /// - alpha: alpha tensor object
+// public convenience init(computationDelegate: OperatorCalculationDelegate? = nil, alpha: [Tensor]) {
+// self.init(computationDelegate: computationDelegate)
+// self.alpha = alpha
+// }
+//
+// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// // MARK: - Methods
+//
+// /// Additional checking needs to verify the diemsions mathcing of `alpha` and `inputTensors`.
+// ///
+// /// - Note: If `alpha` is `nil`, this function will generate default `alpha` following `defaultAlphaValue`.
+// ///
+// /// - Returns: return pass or not and error message.
+// public override func inputOutputTensorsCheck() -> (check: Bool, msg: String) {
+// // super check
+// let (pass, msg) = super.inputOutputTensorsCheck()
+// guard pass else {
+// // false return
+// return (pass, msg)
+// }
+//
+// // check alpha
+// if self.alpha == nil {
+// // generate default alpha
+// SerranoLogging.stdLogging(message: "Operator \(self.operatorLabel) has nil alpha. Will generate default alpha tensors with value \(self.defaultAlphaValue).",
+// file: "\(#file)", function: "\(#function)", line: "\(#line)",
+// loggingLevel: SerranoLoggingType.LowLevel)
+// self.alpha = [Tensor]()
+// for tensor in self.inputTensors! {
+// self.alpha!.append(Tensor(repeatingValue: self.defaultAlphaValue, tensorShape: tensor.shape))
+// }
+// } else {
+// // check matching
+// guard self.alpha!.count == self.inputTensors!.count else {
+// return (false, "Attribute alpha does not have same number of tensor objects as input tensors does.")
+// }
+// for (alphaTensor, inputTensor) in zip(self.alpha!, self.inputTensors!) {
+// guard alphaTensor.shape .== inputTensor.shape else {
+// return (false, "Alpha has shape \(alphaTensor.shape.description) while input tensor has shape \(inputTensor.shape.description).")
+// }
+// }
+// }
+//
+// return (true, "")
+// }
+//
+// /// Attribute `alpha` as an array of `TensorSymbol` if not `nil`.
+// /// Attribute `defaultAlphaValue` as a `ScalarSymbol`.
+// ///
+// /// - Returns: Array of GraphSymbol
+// public override func paramSymbols() -> [GraphSymbol] {
+// let defaultAlphaValue = SerranoScalarSymbol("defaultAlphaValue", dataType: .float, dataSource: .Default)
+// defaultAlphaValue.bindedData = Float(0.3)
+//
+// var symbols = [defaultAlphaValue]
+//
+// if self.alpha != nil {
+// for tensor in self.alpha! {
+// symbols.append(SerranoTensorSymbol( )
+// }
+// }
+//
+// return symbols
+// }
+//
+// /// Override
+// internal override func cpu() {
+// let workGroup = DispatchGroup()
+// for i in 0...size)
+// guard countBuffer != nil else {
+// fatalError("[Serrano] Failed to careate MTLBuffer.")
+// }
+// SerranoLogging.stdLogging(message: "Allocated a Metal buffer [\(countBuffer!.length) bytes] requested for count info \(count) by operator \(self.operatorLabel)", file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+//
+// // encoder
+// let encoder = commandBuffer!.makeComputeCommandEncoder()
+// encoder.setComputePipelineState(kernel!)
+// encoder.setBuffer(inputBuffers[bufferIndex].buffer, offset: inputBuffers[bufferIndex].offset, at: 0)
+// encoder.setBuffer(resultBuffers[bufferIndex].buffer, offset: resultBuffers[bufferIndex].offset, at: 1)
+// encoder.setBuffer(countBuffer, offset: 0, at: 2)
+// encoder.setBuffer(alphaBuffers[bufferIndex].buffer, offset: alphaBuffers[bufferIndex].offset, at: 3)
+//
+// // dispatch
+// let threadsPerThreadgroup = MTLSizeMake(kernel!.threadExecutionWidth,
+// 1,
+// 1)
+// let threadgroupsPerGrid = MTLSizeMake((self.inputTensors![bufferIndex].count + threadsPerThreadgroup.width - 1) / threadsPerThreadgroup.width,
+// 1,
+// 1)
+// encoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup)
+// SerranoLogging.stdLogging(message: "Dispatch group configured with threadgroupsPerGrid: \(threadgroupsPerGrid), threadsPerThreadgroup: \(threadsPerThreadgroup) requested by operator \(self.operatorLabel)", file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+// encoder.endEncoding()
+// }
+//
+// // commit command buffer
+// commandBuffer!.commit()
+// commandBuffer!.waitUntilCompleted()
+// }
+//}
+
diff --git a/Source/Serrano/operators/nn/common/batchnorm_op.swift b/Source/Serrano/operators/nn/common/batchnorm_op.swift
new file mode 100644
index 0000000..bae0590
--- /dev/null
+++ b/Source/Serrano/operators/nn/common/batchnorm_op.swift
@@ -0,0 +1,23 @@
+//
+// batchnorm_op.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 8/14/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import Metal
+import Dispatch
+import Accelerate
+
+//TODO: Implementation
+
+///**
+//Batch normalization operator.
+//
+//Normalize on input tensors by each tensor's mean and variance.
+//*/
+//public class BatchNormOperator: ComputableOperator {
+//
+//}
diff --git a/Source/Serrano/operators/nn/common/fullyconnected_op.metal b/Source/Serrano/operators/nn/common/fullyconnected_op.metal
new file mode 100644
index 0000000..e5fa30c
--- /dev/null
+++ b/Source/Serrano/operators/nn/common/fullyconnected_op.metal
@@ -0,0 +1,77 @@
+//
+// fullyconnected_op.metal
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/19/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+#include
+using namespace metal;
+
+typedef struct
+{
+ uint M; // number of rows in A
+ uint N; // number of cols in B
+ uint K; // number of cols in A, number of rows in B
+ ushort stride; // Element stride in bytes
+ bool useBias; // 0 - false, 1 - true
+} FCInfo;
+
+
+kernel void Fullyconnected(constant float* input_A [[ buffer(0) ]],
+ constant float* input_B [[ buffer(1) ]], // should be transposed
+ const device float* C [[ buffer(2) ]],
+ constant float* biasArray [[ buffer(3) ]],
+ constant FCInfo& info [[ buffer(4) ]],
+ uint2 gid [[ thread_position_in_grid ]]) {
+ uint M = info.M;
+ uint N = info.N;
+ uint K = info.K;
+ ushort stride = info.stride;
+ bool useBias = info.useBias;
+
+ // check boundary
+ if (gid.x >= M || gid.y >= N) return;
+
+ device float* c_reader = (device float*)((device char*)C + gid.x * N * stride + gid.y * stride);
+ c_reader[0] = (useBias ? biasArray[gid.y] : 0.0f);
+
+ // small case
+ if (K < 4) {
+ constant float* a_reader = (constant float*)((constant char*)input_A + gid.x * K * stride);
+ constant float* b_reader = (constant float*)((constant char*)input_B + gid.y * K * stride);
+ ushort i = 0;
+ while (i < K) {
+ c_reader[0] += a_reader[i] * b_reader[i];
+ i++;
+ }
+ return;
+ }
+
+ // regular case, each time read 4 elements
+ constant float4* a_reader = (constant float4*)((constant char*)input_A + gid.x * K * stride);
+ constant float4* b_reader = (constant float4*)((constant char*)input_B + gid.y * K * stride);
+ uint align_bound = K / 4;
+ ushort i = 0;
+ float4 result;
+ while (i < align_bound) {
+ result = a_reader[i] * b_reader[i];
+ c_reader[0] += result.x;
+ c_reader[0] += result.y;
+ c_reader[0] += result.z;
+ c_reader[0] += result.w;
+ i++;
+ }
+
+ // rest
+ if (K % 4 != 0) {
+ constant float* a_reader_p = (constant float*)((constant char*)input_A + gid.x * K * stride);
+ constant float* b_reader_p = (constant float*)((constant char*)input_B + gid.y * K * stride);
+ i = align_bound * 4;
+ while (i < K) {
+ c_reader[0] += a_reader_p[i] * b_reader_p[i];
+ i++;
+ }
+ }
+}
diff --git a/Source/Serrano/operators/nn/common/fullyconnected_op.swift b/Source/Serrano/operators/nn/common/fullyconnected_op.swift
new file mode 100644
index 0000000..867081e
--- /dev/null
+++ b/Source/Serrano/operators/nn/common/fullyconnected_op.swift
@@ -0,0 +1,574 @@
+//
+// fullyconnected_op.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/16/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import Metal
+import Dispatch
+import Accelerate
+
+/// This struct corresponds to the `FCInfo` struct in file 'fullyconnected_op.metal'
+public struct FCInfo {
+ var M: MetalUInt // number of rows in A
+ var N: MetalUInt // number of cols in B
+ var K: MetalUInt // number of cols in A, number of rows in B
+ var stride: MetalUShort // element stride in bytes
+ var useBias: Bool // 1 - true, 0 - false
+}
+
+/**
+The regular fully connected operator.
+Operator do the `1D_dot(inputTensor.flatten, weights) + bias` calculation on all input tensors.
+
+## Weight tensor layout
+The `weight` is a 2D tensor with shape: `[n, m]` where :
+- `n`: the flattened dimension of `inputTensors`, i.e. same value as `count` of a input tensor.
+- `m`: the number of hidden nodes, same value as `numUnits`;
+
+Thus, each column stores the weights of corresponding hidden unit.
+
+## Bias tensor layout
+The `bias` is a 1D tensor with shape `[m]` where:
+- `m`: the number of hidden nodes, same value as `numUnits`;
+
+Thus, each value in the tensor is the bias value of corresponding hidden unit.
+
+## Input tensors auto flatten
+For input tensor with rank `>=2`, the operator will automatically flatten the tensor and then do calcualtion.
+
+## Multiple input tensors
+If `inputTensors` has more than 1 tensor object,
+the operator applies calculation on each input tensor independently
+and stores the results in corresponding tensor of `outputTensors`.
+
+- Note: All input tensor should have same `count`.
+
+## Bias enable choice
+Bias could be disabled by setting the `biasEnabled` to `false`.
+
+## Batch calculation
+This operator itself does not explicitly support batch calculation.
+But user can use slice tensor to do the same thing.
+Details can be found in [Slice tensor]() and [Batch calculation with operators]()
+*/
+public class FullyconnectedOperator: ComputableOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// Operator label. Conforms to `ComputableOperator`
+ public var operatorLabel: String
+
+ /// This operator does not operator on GPU. Conforms to `ComputableOperator`
+ public var metalKernelFuncLabel:String = "Fullyconnected"
+
+ /// Conforms to `ComputableOperator`
+ public var computationDelegate: OperatorCalculationDelegate?
+
+ /// Conforms to `ComputableOperator`
+ public var inputTensors: [Tensor]?
+
+ /// Conforms to `ComputableOperator`
+ public var outputTensors: [Tensor]?
+
+ /// If use `bias`. Default is `true`.
+ public var biasEnabled: Bool = true
+
+ /// Weight tensor.
+ ///
+ /// ## Shape specific
+ /// The tensor should be with shape `[inputDim, numUnits]`.
+ ///
+ /// - Note: If `weight` is `nil` when calculation, `fataError` will be raised.
+ public var weight: Tensor?
+
+ /// Bias tensor
+ ///
+ /// ## Shape specific
+ /// The tensor should be with shape `[numUnits]`.
+ ///
+ /// - Note: If `bias` is `nil` when calculation, `fataError` will be raised.
+ public var bias: Tensor?
+
+ /// Number of input units. Must be a positive integer.
+ public var inputDim: Int = 1 {
+ didSet {
+ if numUnits <= 0 {
+ SerranoLogging.errorLogging(message: "Attribute inputDim of FullyconnectedOperator must be a positive integer. " +
+ "Given \(numUnits).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ }
+ }
+
+ /// Number of hidden units. Must be a positive integer.
+ public var numUnits: Int = 1 {
+ didSet {
+ if numUnits <= 0 {
+ SerranoLogging.errorLogging(message: "Attribute numUnits of FullyconnectedOperator must be a positive integer. " +
+ "Given \(numUnits).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ }
+ }
+
+ /// If `true`, operator will not check the `upGrads`'s shape.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ /// Cases like auto generated differentiation graph.
+ public var disableUpGradShapeCheck: Bool = false
+
+ /// If `true`, operator will not call `inputOutputTensorsCheck()` before doing calculation.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ public var disableInputOutputCheck: Bool = false
+
+ /// Indicate if this operator would do paramter update.
+ public var trainable: Bool = true
+
+ /// The mapping type of this operator.
+ /// `OneToOne` for this operator.
+ public var mapType: OperatorMappingType {
+ get {
+ return OperatorMappingType.OneToOne
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+ /// Designated init
+ ///
+ /// - Parameters:
+ /// - inputDim: inputDim
+ /// - numUnits: numUnits
+ /// - operatorLabel: operatorLabel
+ /// - inputTensors: inputTensors
+ /// - outputTensors: outputTensors
+ /// - computationDelegate: computationDelegate
+ /// - weight: weight
+ /// - bias: bias
+ public init(inputDim: Int, numUnits: Int, operatorLabel: String, inputTensors: [Tensor]?, outputTensors: [Tensor]?,
+ computationDelegate: OperatorCalculationDelegate?, weight: Tensor?, bias: Tensor?) {
+ guard numUnits >= 1 else {
+ SerranoLogging.errorLogging(message: "Attribute numUnits of FullyconnectedOperator must be a positive integer. " +
+ "Given \(numUnits).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ guard inputDim >= 1 else {
+ SerranoLogging.errorLogging(message: "Attribute inputDim of FullyconnectedOperator must be a positive integer. " +
+ "Given \(numUnits).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ self.inputDim = inputDim
+ self.numUnits = numUnits
+ self.operatorLabel = operatorLabel
+ self.inputTensors = inputTensors
+ self.outputTensors = outputTensors
+ self.computationDelegate = computationDelegate
+ self.weight = weight
+ self.bias = bias
+ }
+
+ /// Convenience init
+ ///
+ /// - Parameters:
+ /// - inputDim: inputDim
+ /// - numUnits: numUnits
+ /// - inputTensors: inputTensors
+ /// - outputTensors: outputTensors
+ /// - computationDelegate: computationDelegate
+ public convenience init(inputDim: Int, numUnits: Int,
+ operatorLabel: String = "FullyconnectedOperator",
+ inputTensors: [Tensor]? = nil, outputTensors: [Tensor]? = nil,
+ computationDelegate: OperatorCalculationDelegate? = nil) {
+ self.init(inputDim: inputDim, numUnits: numUnits, operatorLabel: operatorLabel,
+ inputTensors: inputTensors, outputTensors: outputTensors,
+ computationDelegate: computationDelegate, weight: nil, bias: nil)
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+ /// The output shape of `FullyConnectedOperator` is decides by `numUnits` and `inputDim`.
+ ///
+ /// - Parameter shapes: input shapes
+ /// - Returns: result shapes
+ public func outputShape(shapeArray shapes: [TensorShape]) -> [TensorShape]? {
+ // empty check
+ guard shapes.count >= 1 else {
+ SerranoLogging.errorLogging(message: "Input shapes array is empty.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ // all shapes should equal to inputDim
+ for shape in shapes {
+ guard self.inputDim == shape.count else {
+ SerranoLogging.errorLogging(message: "Input shape's count(\(shape.count)) should equal to inputDim(\(self.inputDim)).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+ }
+
+ var outShapes = [TensorShape]()
+ for shape in shapes {
+ outShapes.append(TensorShape(dataType: shape.dataType, shape: [self.numUnits]))
+ }
+ return outShapes
+ }
+
+
+ /// Check validation of `inputTensors`, `outputTensors`, `weight` and `bias`.
+ ///
+ /// - Returns: check, if pass. msg, error message.
+ public func inputOutputTensorsCheck() -> (check: Bool, msg: String) {
+ // input tensor not nil
+ guard self.inputTensors != nil else {
+ return (false, "Input tensors array is nil.")
+ }
+
+ // output tensor not nil
+ guard self.outputTensors != nil else {
+ return (false, "Output tensors array is nil.")
+ }
+
+ // weight not nil
+ guard self.weight != nil else {
+ return (false, "Weight tensor is nil.")
+ }
+
+ // bias not nil if enabled
+ if self.biasEnabled {
+ guard self.bias != nil else {
+ return (false, "Bias is nil.")
+ }
+ }
+
+ // check input shapes
+ let inputShapes = self.inputTensors!.map { $0.shape }
+ let outputShapesCheck = self.outputShape(shapeArray: inputShapes)
+ guard outputShapesCheck != nil else {
+ return (false, "Input tensors are invalid. Check log for details.")
+ }
+
+ // check output shape
+ let outputShapes = self.outputTensors!.map { $0.shape }
+ guard outputShapes.count == inputShapes.count else {
+ // same count
+ return (false, "Input tensors and output tensors have different number of objects. " +
+ "Input tensors contains \(inputShapes.count) tensors, output tensor contains \(outputShapes.count) tensors.")
+ }
+ for (shape, shapeCheck) in zip(outputShapes, outputShapesCheck!) {
+ // same shape
+ guard shape == shapeCheck else {
+ return (false, "One of output tensors has invalid shape. Expect \(shapeCheck.description), give \(shape.description).")
+ }
+ }
+
+ // check weights dim
+ guard self.weight!.rank == 2 else {
+ // rank
+ return (false, "Weight tensor has invalid rank. Expect 2, given \(self.weight!.rank).")
+ }
+ guard self.weight!.shape.shapeArray[0] == self.inputDim && self.weight!.shape.shapeArray[1] == self.numUnits else {
+ // match with input
+ return (false, "Weight shape is invalid. Expect [\(self.inputDim), \(self.numUnits)], given \(self.weight!.shape.shapeArray).")
+ }
+
+ // check bias dim
+ if self.biasEnabled {
+ guard self.bias!.rank == 1 else {
+ // rank
+ return (false, "Bias tensor has invalid rank. Expect 1, given \(self.bias!.rank).")
+ }
+ guard self.bias!.shape.shapeArray[0] == self.numUnits else {
+ // shape match with numUnits
+ return (false, "Bias tensor shape is invalid. Expect [\(self.numUnits)], given [\(self.bias!.shape.shapeArray[0])].")
+ }
+ }
+
+ return (true, "")
+ }
+
+ /// Compute synclly.
+ ///
+ /// - Parameters:
+ /// - tensors: input tensors
+ /// - computationMode: cmputation mode. If choose `GPU` but haven't configued a GPU SerranoEngine, operator will use `CPU` to compute.
+ /// - Returns: result tensors
+ public func compute(_ computationMode: OperatorComputationMode = SerranoEngine.configuredEngine.defaultComputationMode) {
+ // check
+ let (pass, msg) = self.inputOutputTensorsCheck()
+ guard pass else {
+ SerranoLogging.errorLogging(message: "Operator \(self.operatorLabel) aborts calculation cause given invalid data: \(msg)", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+
+ self.computationDelegate?.operatorWillBeginComputation(self)
+
+ switch computationMode {
+ case .GPU:
+ if !SerranoEngine.configuredEngine.hasAvailableGPU() {
+ SerranoLogging.warningLogging(message: "Serrano Engine has no available configured GPU device. Use CPU doing calculation instead.", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ self.cpu()
+ } else {
+ self.gpu()
+ }
+ case .CPU:
+ self.cpu()
+ case .Auto:
+ // TODO: More intelligent way to decide
+ if self.inputTensors![0].count > 1000000 && SerranoEngine.configuredEngine.hasAvailableGPU(){
+ self.gpu()
+ } else {
+ self.cpu()
+ }
+ }
+ self.computationDelegate?.operatorDidEndComputation(self, outputTensors: self.outputTensors!)
+ }
+
+ /// Compute asynclly
+ ///
+ /// - Parameters:
+ /// - tensors: input tensors
+ /// - computationMode: computation mode
+ public func computeAsync(_ computationMode: OperatorComputationMode = SerranoEngine.configuredEngine.defaultComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.compute(computationMode)
+ }
+ }
+
+ /// Calulate grads sync.
+ /// All unary operator return grads tensor with same number and shape as attribute `inputTensors`.
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ /// - Returns: return grads tensor
+ public func gradCompute(_ computationMode: OperatorComputationMode) -> [String: DataSymbolSupportedDataType] {
+ //TODO: Implementation
+ fatalError("Not implemented")
+ }
+
+ /// Cal grads async
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ public func gradComputAsync(_ computationMode: OperatorComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ _ = self.gradCompute(computationMode)
+ }
+ }
+
+ /// Update params if possible.
+ /// No update parameters for binary operators.
+ ///
+ /// - Parameters:
+ /// - grads: grads tensor list
+ /// - LR: learning rate
+ public func updateParams(grads: [Tensor], LR: Float) {
+ return
+ }
+
+ /// Bind according to labels.
+ ///
+ /// -Note: if cannot bind all needed parameters. `fatalError` will be raised.
+ public func bindParamSymbols(_ symbols: [GraphSymbol]) {
+ var paramsLabels = ["weight"]
+ if self.biasEnabled {
+ paramsLabels.append("bias")
+ }
+
+ for label in paramsLabels {
+ let symbol = (symbols.filter {$0.symbolLabel == label}).first
+ guard symbol != nil else{
+ SerranoLogging.errorLogging(message: "\(label) symbol does not exist.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("Faltal error raised by Serrano. Check log for details.")
+ }
+ guard symbol!.symbolType == SymbolType.Tensor else {
+ SerranoLogging.errorLogging(message: "\(label) symbol should be a tensor symbol.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("Faltal error raised by Serrano. Check log for details.")
+ }
+ let dataSymbol = symbol! as! TensorSymbol
+ guard dataSymbol.bindedData != nil else {
+ SerranoLogging.errorLogging(message: "\(label) symbol has no binded data.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("Faltal error raised by Serrano. Check log for details.")
+ }
+ if label == "weight" {
+ self.weight = dataSymbol.bindedData! as! Tensor
+ } else {
+ self.bias = dataSymbol.bindedData! as! Tensor
+ }
+ }
+ }
+
+ /// Attribute `weight` as a `TensorSymbol`.
+ /// Attribute `bias` as a `TensorSymbol`.
+ ///
+ /// - Returns: Array of GraphSymbol
+ public func paramSymbols() -> [GraphSymbol] {
+ // These labels are important for bindParamSymbols(:)
+ let weight = SerranoTensorSymbol("weight", dataSource: .User, shape: TensorShape(dataType: .float, shape: [self.inputDim, self.numUnits]))
+ let bias = SerranoTensorSymbol("bias", dataSource: .User, shape: TensorShape(dataType: .float, shape: [self.numUnits]))
+
+ return [weight, bias]
+ }
+
+ /// Just do the matrix multiplication between each inpute tensor and weight tensor.
+ /// Then add bias.
+ internal func cpu() {
+ let weightAddress = self.weight!.contentsAddress
+ let biasAddress = self.bias?.contentsAddress
+
+ for (inputTensor, outputTensor) in zip(self.inputTensors!, self.outputTensors!) {
+ let inputAddress = inputTensor.contentsAddress
+ let outputAddress = outputTensor.contentsAddress
+
+ // weights
+ let M = Int32(1)
+ let N = Int32(self.weight!.shape.shapeArray[1])
+ let K = Int32(inputTensor.count)
+
+ let startTime = CFAbsoluteTimeGetCurrent()
+
+ cblas_sgemm(CblasRowMajor, cblasTrans(false), cblasTrans(false), M, N, K,
+ 1.0, inputAddress, K, weightAddress, N, 0.0, outputAddress, N)
+
+ // bias
+ if self.biasEnabled {
+ let count = vDSP_Length(outputTensor.count)
+ vDSP_vadd(outputAddress, 1, biasAddress!, 1, outputAddress, 1, count)
+ }
+
+ let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
+ SerranoLogging.stdLogging(message: "Executed CPU calcualtion in \(timeElapsed) seconds.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+
+
+ }
+ }
+
+ /// We use op
+ internal func gpu() {
+ let workGroup = DispatchGroup()
+ let weightTensor = self.weight!
+
+ // cal for each tensor
+ for (inputTensor, outputTensor) in zip(self.inputTensors!, self.outputTensors!) {
+ workGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+
+ // transpose weight
+ let transposeWeight = SerranoResourceManager.globalManager.allocateUnamangedTensor(weightTensor.shape.transposed())
+ let transOp = TransposeOperator(inputTensors: [weightTensor], outputTensors: [transposeWeight])
+ transOp.compute(.GPU)
+
+ // prepare resources
+ let resourcePrepareGroup = DispatchGroup()
+ let engine = SerranoEngine.configuredEngine
+ var kernel: MTLComputePipelineState?
+ var commandBuffer: MTLCommandBuffer?
+ var dataBuffers: [MTLBufferResource] = [MTLBufferResource]()
+ var infoBuffer: MTLBuffer?
+
+ // kernel
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ var info = ""
+ (kernel, info) = engine.loadGPUKernel(kernelLabel: self.metalKernelFuncLabel)
+ guard kernel != nil else {
+ fatalError("[Serrano] Failed to load kernel \(self.metalKernelFuncLabel). Info: \(info)")
+ }
+ resourcePrepareGroup.leave()
+ }
+
+ // command buffer
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ commandBuffer = engine.serranoCommandQueue?.makeCommandBuffer()
+ guard commandBuffer != nil else {
+ fatalError("[Serrano] Failed to make new command buffer.")
+ }
+ resourcePrepareGroup.leave()
+ }
+
+ //// Prepare MTLBuffers
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ if self.biasEnabled {
+ dataBuffers = SerranoResourceManager.globalManager.allocateMTLBufferResources([inputTensor, transposeWeight,
+ outputTensor, self.bias!])
+ } else {
+ dataBuffers = SerranoResourceManager.globalManager.allocateMTLBufferResources([inputTensor, transposeWeight,
+ outputTensor])
+ }
+ resourcePrepareGroup.leave()
+ }
+
+ /// FCInfo buffer
+ /// Here, no matter what shape the input tensor has, we view it as a [1, count] matrix.
+ var info = FCInfo(M: MetalUInt(1),
+ N: MetalUInt(weightTensor.shape.shapeArray[1]),
+ K: MetalUInt(inputTensor.count),
+ stride: MetalUShort(MemoryLayout.stride),
+ useBias: self.biasEnabled)
+ infoBuffer = engine.GPUDevice?.makeBuffer(bytes: &info, length: MemoryLayout.stride, options: .storageModeShared)
+ guard infoBuffer != nil else { fatalError("[Serrano] Failed to careate MTLBuffer.") }
+ SerranoLogging.stdLogging(message: "Allocated a Metal buffer [\(infoBuffer!.length) bytes] requested for matrix dimentsion info info \(info) by operator \(self.operatorLabel)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+
+ resourcePrepareGroup.wait()
+
+ //// Encoders.
+ let encoder = commandBuffer!.makeComputeCommandEncoder()
+ encoder.setComputePipelineState(kernel!)
+ encoder.setBuffer(dataBuffers[0].buffer, offset: dataBuffers[0].offset, at: 0)
+ encoder.setBuffer(dataBuffers[1].buffer, offset: dataBuffers[1].offset, at: 1)
+ encoder.setBuffer(dataBuffers[2].buffer, offset: dataBuffers[2].offset, at: 2)
+ if self.biasEnabled { encoder.setBuffer(dataBuffers[3].buffer, offset: dataBuffers[3].offset, at: 3) }
+ encoder.setBuffer(infoBuffer, offset: 0, at: 4)
+ /// Calculate grid
+ let threadsPerThreadgroup = MTLSizeMake(1,
+ kernel!.threadExecutionWidth,
+ 1)
+ /// virew output tensor as a [1, numUnit] matrix
+ let threadgroupsPerGrid = MTLSizeMake(1,
+ (outputTensor.shape.shapeArray[0] + threadsPerThreadgroup.height - 1) / threadsPerThreadgroup.height,
+ 1)
+ encoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup)
+ SerranoLogging.stdLogging(message: "Dispatch group configured with threadgroupsPerGrid: \(threadgroupsPerGrid), threadsPerThreadgroup: \(threadsPerThreadgroup) requested by operator \(self.operatorLabel)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+
+ encoder.endEncoding()
+
+ // commit command buffer
+ commandBuffer!.commit()
+
+ let startTime = CFAbsoluteTimeGetCurrent()
+ commandBuffer!.waitUntilCompleted()
+ let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
+ SerranoLogging.stdLogging(message: "Executed GPU calcualtion in \(timeElapsed) seconds.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+
+ workGroup.leave()
+ }
+ }
+
+ workGroup.wait()
+ }
+
+}
diff --git a/Source/Serrano/operators/nn/convolution/conv_op.metal b/Source/Serrano/operators/nn/convolution/conv_op.metal
new file mode 100644
index 0000000..bbf4d38
--- /dev/null
+++ b/Source/Serrano/operators/nn/convolution/conv_op.metal
@@ -0,0 +1,27 @@
+//
+// conv_op.metal
+// serrano
+//
+// Created by ZHONGHAO LIU on 10/26/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+#include
+using namespace metal;
+
+/**
+ Naive convolutional 2D.
+ Each thread calculate one value in output tensor.
+ */
+
+//TODO: implement
+
+
+//void kernel conv2d_naive(constant float* input [[ buffer(0) ]],
+// device float* output [[ buffer(1) ]],
+// constant Img2ColInfo& info [[ buffer(2) ]],
+// uint3 group_id [[ threadgroup_position_in_grid ]],
+// uint3 thread_id_group [[ thread_position_in_threadgroup ]]) {
+//
+//}
+
diff --git a/Source/Serrano/operators/nn/convolution/conv_op.swift b/Source/Serrano/operators/nn/convolution/conv_op.swift
new file mode 100644
index 0000000..6d87199
--- /dev/null
+++ b/Source/Serrano/operators/nn/convolution/conv_op.swift
@@ -0,0 +1,476 @@
+//
+// conv_op.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 8/14/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import Metal
+import Dispatch
+import Accelerate
+
+// TODO: figure out if needs inputshape for same on all inputs
+
+/**
+2D Convolution operator.
+
+## Input tensors shapes
+All tensors in `inputTensors` should have same shapes
+
+## Shape specification
+- inputTensors: each tensor: `[channel, height, width]` or `[height, width, channel]` according to `channelPosition`
+- weight: `[num_filter,channel, kernelSize[0], kernelSize[1]]`,
+- outputTensors: each tensor: `[out_height, out_width, num_filter]`, i.e., `TensorChannelOrder.Last`
+
+## nil weight
+At declaration, `weight` could be `nil`.
+However, if you add this operator through a graph's `operation(_,inputs:,op:)` API (i.e. symbolic constructing graph).
+You must indicate the `inputShape` attribute so that the graph could estimate the input and output shape information.
+
+## Batch process
+This operator does not directly support batch data.
+However, user could use `TensorSlice` to do the batch processing.
+Details can be found in [Slice tensor]() and [Batch calculation with operators]().
+
+## Dilation
+Currently, calculation with `dilation > 1` has not been implemented and supported.
+
+*/
+public class ConvOperator2D: ComputableOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// Operator label. Conforms to `ComputableOperator`
+ public var operatorLabel: String
+
+ /// This operator does not operator on GPU. Conforms to `ComputableOperator`
+ public var metalKernelFuncLabel:String = "Conv2D"
+
+ /// Conforms to `ComputableOperator`
+ public var computationDelegate: OperatorCalculationDelegate?
+
+ /// Conforms to `ComputableOperator`
+ public var inputTensors: [Tensor]?
+
+ /// Conforms to `ComputableOperator`
+ public var outputTensors: [Tensor]?
+
+ /// If `true`, operator will not call `inputOutputTensorsCheck()` before doing calculation.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ public var disableInputOutputCheck: Bool
+
+ /// Indicate if this operator would do paramter update.
+ public var trainable: Bool = true
+
+ /// The mapping type of this operator.
+ /// `OneToOne` for this operator.
+ public var mapType: OperatorMappingType {
+ get {
+ return OperatorMappingType.OneToOne
+ }
+ }
+
+ /// The number of fitlers
+ public var numFilters: Int
+
+ /// The kernel size. `[height, width]`
+ public var kernelSize: [Int]
+
+ /// Stride. `[height, width]`
+ /// Default is `[1, 1]`.
+ public var stride: [Int]
+
+ /// Dilate values. 2D vecotr indicating the dilation value in height and width.
+ /// Default is `[1, 1]`, i.e. without dilation.
+ /// TODO: Support dilation
+ public var dilation: [Int]
+
+ /// Padding mode. Default `PaddingMode.valid`
+ public var padMode: PaddingMode = PaddingMode.Valid
+
+ /// Channel position. Default is `ImageChannelOrder.First`
+ public var channelPosition: TensorChannelOrder = .First
+
+ /// The weight tensor.
+ public var weight: Tensor?
+
+ /// The input shape
+ /// Used to indicate the input tensors' shape.
+ /// Should not be `nil` construction from scratch.
+ public var inputShape: TensorShape?
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Init
+
+ /// Designated init.
+ ///
+ /// - Parameters:
+ /// - numFilters:
+ /// - kernelSize:
+ /// - stride:
+ /// - padMode:
+ /// - channelPosition:
+ /// - weight:
+ /// - dilation:
+ /// - computationDelegate:
+ /// - inputTensors:
+ /// - outputTensors:
+ /// - operatorLabel:
+ /// - inputShape:
+ /// - disableInputOutputCheck:
+ public init(numFilters: Int, kernelSize: [Int],
+ stride: [Int] = [1, 1],
+ padMode: PaddingMode = .Valid,
+ channelPosition: TensorChannelOrder = .First,
+ weight: Tensor? = nil,
+ dilation: [Int] = [1, 1],
+ computationDelegate: OperatorCalculationDelegate? = nil,
+ inputTensors: [Tensor]? = nil, outputTensors: [Tensor]? = nil,
+ operatorLabel: String = "Conv2DOp",
+ inputShape: TensorShape? = nil,
+ disableInputOutputCheck: Bool = false) {
+ self.numFilters = numFilters
+ self.kernelSize = kernelSize
+ self.stride = stride
+ self.dilation = dilation
+ self.padMode = padMode
+ self.channelPosition = channelPosition
+ self.weight = weight
+ self.computationDelegate = computationDelegate
+ self.inputTensors = inputTensors
+ self.outputTensors = outputTensors
+ self.operatorLabel = operatorLabel
+ self.inputShape = inputShape
+ self.disableInputOutputCheck = disableInputOutputCheck
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+ /// Compute output shape according `numFilters`, `kernelSize`, `stride` and `dilation`.
+ ///
+ /// - Parameter shapes: shapes description
+ /// - Returns: return value description
+ public func outputShape(shapeArray shapes: [TensorShape]) -> [TensorShape]? {
+ // shape empty check
+ guard shapes.count >= 1 else {
+ SerranoLogging.errorLogging(message: "Input shapes array is empty",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ // numFilters check
+ guard numFilters > 0 else {
+ SerranoLogging.errorLogging(message: "numFilters (\(self.numFilters)) should be a positive integer",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ // kernelSize check
+ guard self.kernelSize.count == 2 && self.kernelSize[0] > 0 && self.kernelSize[1] > 0 else {
+ SerranoLogging.errorLogging(message: "Invalid kernelSize (\(self.kernelSize)).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ // stride check
+ guard self.stride.count == 2 && self.stride[0] > 0 && self.stride[1] > 0 else {
+ SerranoLogging.errorLogging(message: "Invalid stride (\(self.stride)).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ // dilation check
+ guard self.dilation.count == 2 && self.dilation[0] >= 1 && self.dilation[1] >= 1 else {
+ SerranoLogging.errorLogging(message: "Invalid dilation (\(self.dilation)).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ // input shapes same check
+ let checkShape = shapes.first!
+ for shape in shapes {
+ guard shape == checkShape else {
+ SerranoLogging.errorLogging(message: "Input shapes should have same shape.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+ }
+
+ var outputShapes = [TensorShape]()
+ for inShape in shapes {
+ // check input shapes
+ let shapeArray = inShape.shapeArray
+ guard inShape.rank == 3 || shapeArray[0] > 0 || shapeArray[1] > 0 || shapeArray[2] > 0 else {
+ SerranoLogging.errorLogging(message: "Invalid input shape \(inShape.description)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+ let (_, height, width) = parseImgChannelShapeInfo(self.channelPosition, shapeArray: shapeArray)
+
+ if self.dilation[0] >= 2 || self.dilation[1] >= 2 {
+ fatalError("Not implemented")
+ } else {
+ let outShapeArray = [kernelScanningOutSize(self.padMode, inputSize: height,
+ kernelSize: self.kernelSize[0], stride: self.stride[0]),
+ kernelScanningOutSize(self.padMode, inputSize: width,
+ kernelSize: self.kernelSize[1], stride: self.stride[1]),
+ self.numFilters]
+ outputShapes.append(TensorShape(dataType: inShape.dataType, shape: outShapeArray))
+ }
+ }
+ return outputShapes
+ }
+
+
+ /// Check input and output tensors.
+ ///
+ /// - Returns: return value description
+ public func inputOutputTensorsCheck() -> (check: Bool, msg: String) {
+ // input nil check
+ guard self.inputTensors != nil && self.inputTensors!.count > 0 else {
+ return (false, "inputTensors is nil or empty.")
+ }
+
+ // output nil check
+ guard self.outputTensors != nil && self.outputTensors!.count > 0 else {
+ return (false, "outputTensors is nil or empty.")
+ }
+
+ guard self.weight != nil else {
+ return (false, "weight is nil.")
+ }
+
+ // weight shape check
+ // [channel, kernelSize[0], kernelSize[1], num_filter]
+ let weightShapeArray = self.weight!.shape.shapeArray
+ let (channel, _, _) = parseImgChannelShapeInfo(self.channelPosition, shapeArray: self.inputTensors!.first!.shape.shapeArray)
+ guard weightShapeArray.count == 4 && weightShapeArray[0] == self.numFilters
+ && weightShapeArray[2] == self.kernelSize[0] && weightShapeArray[3] == self.kernelSize[1]
+ && weightShapeArray[1] == channel else {
+ return (false, "Invalid weight shape, Expect \([self.numFilters, channel, self.kernelSize[0], self.kernelSize[1]]). " +
+ "Given \(weightShapeArray).")
+ }
+
+ // inputShape check
+ let inputShapes = self.inputTensors!.map { $0.shape }
+ let outputShapesCheck = self.outputShape(shapeArray: inputShapes)
+ guard outputShapesCheck != nil else {
+ return (false, "Input tensors' shapes are not valid. Check logs for detail.")
+ }
+
+ // outputshape check
+ let outputShapes = self.outputTensors!.map { $0.shape }
+ guard outputShapes.count == outputShapesCheck!.count else {
+ return (false, "Output tensor's count is not valid. Expect \(outputShapesCheck!.count), given \(outputShapes.count).")
+ }
+ for (outputShape, outputShapeCheck) in zip(outputShapes, outputShapesCheck!) {
+ guard outputShape == outputShapeCheck else {
+ return (false, "OutputTensor with shape [\(outputShape.description)] is not valid. Expect [\(outputShapeCheck.description)].")
+ }
+ }
+
+ return (true, "")
+ }
+
+ /// Compute sync way.
+ ///
+ /// - Parameter computationMode: mode
+ public func compute(_ computationMode: OperatorComputationMode) {
+ // check
+ let (pass, msg) = self.inputOutputTensorsCheck()
+ guard pass else {
+ SerranoLogging.errorLogging(message: "Operator \(self.operatorLabel) aborts calculation cause given invalid data: \(msg)", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+
+ self.computationDelegate?.operatorWillBeginComputation(self)
+
+ switch computationMode {
+ case .GPU:
+ if !SerranoEngine.configuredEngine.hasAvailableGPU() {
+ SerranoLogging.warningLogging(message: "Serrano Engine has no available configured GPU device. Use CPU doing calculation instead.", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ self.cpu()
+ } else {
+ self.gpu()
+ }
+ case .CPU:
+ self.cpu()
+ case .Auto:
+ // TODO: More intelligent way to decide
+ if self.inputTensors![0].count > 1000000 && SerranoEngine.configuredEngine.hasAvailableGPU(){
+ self.gpu()
+ } else {
+ self.cpu()
+ }
+ }
+ self.computationDelegate?.operatorDidEndComputation(self, outputTensors: self.outputTensors!)
+ }
+
+ /// Compute async
+ ///
+ /// - Parameter computationMode: computationMode
+ public func computeAsync(_ computationMode: OperatorComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.compute(computationMode)
+ }
+ }
+
+ public func gradCompute(_ computationMode: OperatorComputationMode) -> [String: DataSymbolSupportedDataType] {
+ fatalError()
+ }
+
+ public func gradComputAsync(_ computationMode: OperatorComputationMode) {
+ fatalError()
+ }
+
+ public func updateParams(grads: [Tensor], LR: Float) {
+ fatalError()
+ }
+
+ /// Bind according to labels.
+ ///
+ /// -Note: if cannot bind all needed parameters. `fatalError` will be raised.
+ public func bindParamSymbols(_ symbols: [GraphSymbol]) {
+ let paramsLabels = ["weight"]
+
+ for label in paramsLabels {
+ let symbol = (symbols.filter {$0.symbolLabel == label}).first
+ guard symbol != nil else{
+ SerranoLogging.errorLogging(message: "\(label) symbol does not exist.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("Faltal error raised by Serrano. Check log for details.")
+ }
+ guard symbol!.symbolType == SymbolType.Tensor else {
+ SerranoLogging.errorLogging(message: "\(label) symbol should be a tensor symbol.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("Faltal error raised by Serrano. Check log for details.")
+ }
+ let dataSymbol = symbol! as! TensorSymbol
+ guard dataSymbol.bindedData != nil else {
+ SerranoLogging.errorLogging(message: "\(label) symbol has no binded data.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("Faltal error raised by Serrano. Check log for details.")
+ }
+
+ self.weight = dataSymbol.bindedData! as! Tensor
+
+ }
+ }
+
+ /// `Weight` as `TensorSymbol`
+ ///
+ /// - Returns:
+ public func paramSymbols() -> [GraphSymbol] {
+ let weightTensorSymbol: SerranoTensorSymbol
+ if self.weight == nil {
+ // refer from input shape
+
+ // not nil
+ guard self.inputShape != nil else {
+ SerranoLogging.errorLogging(message: "Operator \(self.operatorLabel) attribute inputShape is nil while its weight tensor is also ni. Need one of them assigned to construct the graph",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("Serrano error. Check log.")
+ }
+
+ // dim check
+ guard self.inputShape!.rank == 3 else {
+ SerranoLogging.errorLogging(message: "Attribute inputShape has invalid rank. Expect 3. Given \(self.inputShape!.rank)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError("Serrano error. Check log.")
+ }
+ let (channel, _, _) = parseImgChannelShapeInfo(self.channelPosition, shapeArray: self.inputShape!.shapeArray)
+ let weightShape = TensorShape(dataType: .float, shape: [self.numFilters, channel, self.kernelSize[0], self.kernelSize[1]])
+ weightTensorSymbol = SerranoTensorSymbol("weight", dataSource: SymbolDataSource.User, shape: weightShape)
+ } else {
+ weightTensorSymbol = SerranoTensorSymbol("weight", dataSource: SymbolDataSource.User, shape: self.weight!.shape)
+ }
+ return [weightTensorSymbol as GraphSymbol]
+ }
+
+ /// Cpu calculation
+ internal func cpu() {
+ // call dilation function
+ if self.dilation[0] > 1 || self.dilation[1] > 1 {
+ self.cpu_dilation()
+ }
+
+ regular(OperatorComputationMode.CPU)
+ }
+
+ /// GPU calcualtion
+ internal func gpu() {
+ // call dilation function
+ if self.dilation[0] > 1 || self.dilation[1] > 1 {
+ self.gpu_dilation()
+ }
+
+ regular(OperatorComputationMode.GPU)
+ }
+
+ /// Use Img2Col to calcualte result.
+ /// 1. Convert each input tensor via Img2Col to a 2D tensor `A` with shape `[out_Height x out_Width, channel x kernelSize[0] x kernelSize[1]]`;
+ /// 2. We view weight tensor as a 2D tensor `B` with shape `[num_filter,channel x kernelSize[0] x kernelSize[1]]`;
+ /// 3. Do matrix multiplication `AxB` with `transposeB` setting as `true`.
+ ///
+ /// - Parameter mode: computation mode
+ internal func regular(_ mode: OperatorComputationMode) {
+ // temp make weight's shape 2D faking it as a 2D tensor
+ let originWeightShapeArray = self.weight!.shape.shapeArray
+ self.weight!.shape = TensorShape(dataType: self.weight!.shape.dataType,
+ shape: [originWeightShapeArray[0],
+ originWeightShapeArray[1] * originWeightShapeArray[2] * originWeightShapeArray[3]])
+
+ let workGroup = DispatchGroup()
+ for (input, output) in zip(self.inputTensors!, self.outputTensors!) {
+ workGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ // img2col
+ let img2colOP = Img2ColOperator(patchSize: self.kernelSize, stride: self.stride,
+ channelPosition: self.channelPosition,
+ padMode: self.padMode,
+ inputTensors: [input],
+ disableInputOutputCheck: true)
+ let outShape = img2colOP.outputShape(shapeArray: [input.shape])!.first!
+ let colTensor = SerranoResourceManager.globalManager.allocateUnamangedTensor(outShape)
+ img2colOP.outputTensors = [colTensor]
+ img2colOP.compute(mode)
+
+ // fake output tensor shape as a 2D shape
+ let outputShape = output.shape
+ output.shape = TensorShape(dataType: outputShape.dataType,
+ shape: [outputShape.shapeArray[0] * outputShape.shapeArray[1], outputShape.shapeArray[2]])
+
+ // matrix mult
+ let matrixMultOp = MatrixMultOperator(transposeB: true,
+ inputTensors: [colTensor, self.weight!],
+ outputTensors: [output],
+ disableInputOutputCheck: true)
+ matrixMultOp.compute(mode)
+
+ // change back output tensor shape
+ output.shape = outputShape
+
+ workGroup.leave()
+ }
+ }
+ workGroup.wait()
+
+ // change weight's shape info back
+ self.weight!.shape = TensorShape(dataType: self.weight!.shape.dataType, shape: originWeightShapeArray)
+ }
+
+ internal func cpu_dilation() {
+ //TODO: implementaion
+ }
+
+ internal func gpu_dilation() {
+ //TODO: implementaion
+ }
+
+}
diff --git a/Source/Serrano/operators/nn/convolution/img2col_op.metal b/Source/Serrano/operators/nn/convolution/img2col_op.metal
new file mode 100644
index 0000000..b8a285d
--- /dev/null
+++ b/Source/Serrano/operators/nn/convolution/img2col_op.metal
@@ -0,0 +1,105 @@
+//
+// img2col.metal
+// serrano
+//
+// Created by ZHONGHAO LIU on 8/16/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+#include
+using namespace metal;
+
+typedef struct {
+ short channelPosition; // 0 --> First, 1 --> Last
+ short paddingMode; // 0 --> Valid, 1 --> Same
+ float paddingValue;
+ int channels;
+ int inputWidth;
+ int inputHeight;
+ int kernelScanningXPatchCount;
+ int strideWidth;
+ int strideHeight;
+ int patchWidth;
+ int patchHeight;
+} Img2ColInfo;
+
+/// Get channel from input
+enum ChannelPosition {First, Last};
+ChannelPosition getChannelPosition(int channelPosition);
+ChannelPosition getChannelPosition(int channelPosition) {
+ ChannelPosition pos = First;
+ if (channelPosition == 1) {
+ pos = Last;
+ }
+ return pos;
+}
+
+/// Get padding mode
+enum PaddingMode {Valid, Same};
+PaddingMode getPaddingMode(int paddingMode);
+PaddingMode getPaddingMode(int paddingMode) {
+ PaddingMode mode = Valid;
+ if (paddingMode == 1) {
+ mode = Same;
+ }
+ return mode;
+}
+
+/// Each thread copy one value
+void kernel Img2col (constant float* input [[ buffer(0) ]],
+ device float* output [[ buffer(1) ]],
+ constant Img2ColInfo& info [[ buffer(2) ]],
+ uint3 group_id [[ threadgroup_position_in_grid ]],
+ uint3 thread_id_group [[ thread_position_in_threadgroup ]]) {
+
+
+ int strideWidth = info.strideWidth;
+ int strideHeight = info.strideHeight;
+ int channels = info.channels;
+ int patchWidth = info.patchWidth;
+ int patchHeight = info.patchHeight;
+ float paddingValue = info.paddingValue;
+ int inputWidth = info.inputWidth;
+ int inputHeight = info.inputHeight;
+ ChannelPosition channelPosition = getChannelPosition(info.channelPosition);
+ PaddingMode paddingMode = getPaddingMode(info.paddingMode);
+
+ // get thread group info
+ int patchChannelIndex = group_id.z;
+ int patchHightIndex = group_id.y / info.kernelScanningXPatchCount; // patchX in input
+ int patchWidthIndex = group_id.y % info.kernelScanningXPatchCount; // patchY in input
+ int patchElementHightIndex = thread_id_group.y;
+ int patchElementWidthIndex = thread_id_group.x;
+
+ // get input offset
+ int inputOffset;
+ if (channelPosition == First) {
+ int channelCount = inputWidth * inputHeight; // no. of elements in one channel
+ int channelOffset = channelCount * patchChannelIndex; // start of channel
+ int patchOffset = patchHightIndex * (inputWidth * strideHeight) + patchWidthIndex * strideWidth; // start of patch
+ int elementOffset = patchElementHightIndex * inputWidth + patchElementWidthIndex; // locate element in patch
+ inputOffset = channelOffset + patchOffset + elementOffset;
+ } else {
+ int rowCount = channels * inputWidth; // num of element in each row
+ int channelOffset = patchChannelIndex;
+ int patchOffset = patchHightIndex * (rowCount * strideHeight) + patchWidthIndex * strideWidth * channels;
+ int elementOffset = patchElementHightIndex * rowCount + patchElementWidthIndex * channels;
+ inputOffset = channelOffset + patchOffset + elementOffset;
+ }
+
+ // get output offset
+ int patchCount = patchHeight * patchWidth;
+ int outputOffset = (group_id.y * patchCount * channels + patchChannelIndex * patchCount) +
+ (patchElementHightIndex * patchWidth + patchElementWidthIndex);
+
+ // assign
+ int inputElementX = patchHightIndex * strideHeight + patchElementHightIndex;
+ int inputElementY = patchWidthIndex * strideWidth + patchElementWidthIndex;
+ if (inputElementX >= inputHeight || inputElementY >= inputWidth) { // boundary check
+ if (paddingMode == Same) { // only same use padding value
+ output[outputOffset] = paddingValue;
+ }
+ } else {
+ output[outputOffset] = input[inputOffset];
+ }
+}
diff --git a/Source/Serrano/operators/nn/convolution/img2col_op.swift b/Source/Serrano/operators/nn/convolution/img2col_op.swift
new file mode 100644
index 0000000..c4dd85f
--- /dev/null
+++ b/Source/Serrano/operators/nn/convolution/img2col_op.swift
@@ -0,0 +1,491 @@
+//
+// img2col.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 8/16/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import Metal
+import Dispatch
+import Accelerate
+
+/// Corresponding struct `Img2ColInfo` in img2col_op.metal
+public struct Img2ColInfo {
+ /// 0 --> First, 1 --> Last
+ var channelPosition: MetalShort
+
+ /// 0 --> Valid, 1 --> Same
+ var paddingMode: MetalShort
+
+ /// padding value
+ var paddingValue: MetalFloat
+
+ /// number of channels
+ var channels: MetalInt
+
+ /// input width
+ var inputWidth: MetalInt
+
+ /// input height
+ var inputHeight: MetalInt
+
+ /// kernel scanning patch count in X direction
+ var kernelScanningXPatchCount: MetalInt
+
+ /// stride width
+ var strideWidth: MetalInt
+
+ /// stride height
+ var strideHeight: MetalInt
+
+ /// patch width
+ var patchWdith: MetalInt
+
+ /// patch height
+ var patchHeight: MetalInt
+}
+
+/**
+Operator works like img2col in matlab.
+It converts any 3D tensor (`[H, W, C]` or `[C, H, W]`) into a 2D tensor (`[H*W, C*M*N]`) according to patchSize `[M, N]` and stride.
+*/
+public class Img2ColOperator: ComputableOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// Operator label. Conforms to `ComputableOperator`
+ public var operatorLabel: String
+
+ /// This operator does not operator on GPU. Conforms to `ComputableOperator`
+ public var metalKernelFuncLabel:String = "Img2col"
+
+ /// Conforms to `ComputableOperator`
+ public var computationDelegate: OperatorCalculationDelegate?
+
+ /// Conforms to `ComputableOperator`
+ public var inputTensors: [Tensor]?
+
+ /// Conforms to `ComputableOperator`
+ public var outputTensors: [Tensor]?
+
+ /// If `true`, operator will not call `inputOutputTensorsCheck()` before doing calculation.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ public var disableInputOutputCheck: Bool
+
+ /// Indicate if this operator would do paramter update.
+ public var trainable: Bool = true
+
+ /// The mapping type of this operator.
+ /// `OneToOne` for this operator.
+ public var mapType: OperatorMappingType {
+ get {
+ return OperatorMappingType.OneToOne
+ }
+ }
+
+ /// The patch size. 2D vecotr. `[patchHeight, patchWidth]`
+ public var patchSize: [Int]
+
+ /// The stride. 2D vector. Default is `[1, 1]`. `[strideHeight, strideWidth]`
+ public var stride: [Int] = [1, 1]
+
+ /// Channel position. Default is `ImageChannelOrder.First`
+ public var channelPosition: TensorChannelOrder = .First
+
+ /// Padding mode. Default is `PaddingMode.Valid`
+ public var padMode: PaddingMode = .Valid
+
+ /// Padding value
+ public var paddingValue: Float = 0.0
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Init
+
+ /// Designated init
+ ///
+ /// - Parameters:
+ /// - patchSize: [patchHeight, patchWidth]
+ /// - channelPosition: channelPosition description
+ /// - padMode: padMode
+ /// - stride: [strideHeight, strideWidth]
+ /// - computationDelegate: computationDelegate description
+ /// - inputTensors: inputTensors description
+ /// - outputTensors: outputTensors description
+ /// - operatorLabel: operatorLabel description
+ public init(patchSize: [Int], stride: [Int],
+ channelPosition: TensorChannelOrder = .First,
+ padMode: PaddingMode = PaddingMode.Valid,
+ computationDelegate: OperatorCalculationDelegate? = nil,
+ inputTensors: [Tensor]? = nil, outputTensors: [Tensor]? = nil,
+ operatorLabel: String = "Img2ColOp",
+ disableInputOutputCheck: Bool = false) {
+ self.patchSize = patchSize
+ self.channelPosition = channelPosition
+ self.padMode = padMode
+ self.stride = stride
+ self.computationDelegate = computationDelegate
+ self.inputTensors = inputTensors
+ self.outputTensors = outputTensors
+ self.operatorLabel = operatorLabel
+ self.disableInputOutputCheck = disableInputOutputCheck
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+ /// Compute output shape according `numFilters`, `kernelSize`, `stride` and `dilation`.
+ ///
+ /// - Parameter shapes: shapes description
+ /// - Returns: return value description
+ public func outputShape(shapeArray shapes: [TensorShape]) -> [TensorShape]? {
+ // patch size validation check
+ guard self.patchSize.count == 2 && self.patchSize[0] > 0 && self.patchSize[1] > 0 else {
+ SerranoLogging.errorLogging(message: "Invalid patchSize \(self.patchSize).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ // stride check
+ guard self.stride.count == 2 && self.stride[0] > 0 && self.stride[1] > 0 else {
+ SerranoLogging.errorLogging(message: "Invalid stride \(self.stride).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ var outShapes = [TensorShape]()
+ for inShape in shapes {
+ let shapeArray = inShape.shapeArray
+ guard inShape.rank == 3 && shapeArray[0] > 0 && shapeArray[1] > 0 && shapeArray[2] > 0 else {
+ SerranoLogging.errorLogging(message: "Invalid input shape \(inShape.description)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ // get input shape values
+ let (channel, height, width) = parseImgChannelShapeInfo(self.channelPosition, shapeArray: shapeArray)
+
+ // compute output shape array
+ var outWidth = 0
+ var outHeight = 0
+ if self.padMode == .Valid {
+ // In valid mode, if the input size is less than patchSize. We do not accept it
+ guard height >= self.patchSize[0] && width >= self.patchSize[1] else {
+ SerranoLogging.errorLogging(message: "Padding mode is Valid and the input shape \(inShape.description) with width \(width) and height \(height) is not valid with patchSize \(self.patchSize).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+ }
+ outHeight = kernelScanningOutSize(self.padMode, inputSize: height, kernelSize: self.patchSize[0], stride: self.stride[0])
+ outWidth = kernelScanningOutSize(self.padMode, inputSize: width, kernelSize: self.patchSize[1], stride: self.stride[1])
+ // valid out shape
+ guard outHeight > 0 && outWidth > 0 else {
+ SerranoLogging.errorLogging(message: "Input shape \(inShape.description) is not valid which will lead to negative output dimension.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+ outShapes.append(TensorShape(dataType: inShape.dataType, shape: [outHeight * outWidth,
+ self.patchSize[0] * self.patchSize[1] * channel]))
+
+ }
+ return outShapes
+ }
+
+
+ /// Check input and output tensors.
+ ///
+ /// - Returns: return value description
+ public func inputOutputTensorsCheck() -> (check: Bool, msg: String) {
+ // input not nil
+ guard self.inputTensors != nil else {
+ return (false, "Attribute inputTensors is nil")
+ }
+
+ // output not nil
+ guard self.outputTensors != nil else {
+ return (false, "Attribute outputTensors is nil")
+ }
+
+ // input shape check
+ let inputShapes = self.inputTensors!.map { $0.shape }
+ let outputShapeCheck = self.outputShape(shapeArray: inputShapes)
+ guard outputShapeCheck != nil else {
+ return (false, "Input tensors are not valid. Check log for details.")
+ }
+
+ // output shape check
+ let outputShapes = self.outputTensors!.map { $0.shape }
+ guard outputShapes.count == outputShapeCheck!.count else {
+ return (false, "Attribute outputTensors should have same number of tensors as inputTensors. " +
+ "Expect \(self.inputTensors!.count) tensors, given \(self.outputTensors!.count) tensors.")
+ }
+ for (outputShape, checkShape) in zip(outputShapes, outputShapeCheck!) {
+ guard outputShape == checkShape else {
+ return (false, "One of outputTensors has invalid shape. Expect shape \(checkShape.description), given \(outputShape.description)")
+ }
+ }
+
+ return (true, "")
+ }
+
+ /// Compute sync way.
+ ///
+ /// - Parameter computationMode: mode
+ public func compute(_ computationMode: OperatorComputationMode) {
+ // check
+ if !self.disableInputOutputCheck {
+ let (pass, msg) = self.inputOutputTensorsCheck()
+ guard pass else {
+ SerranoLogging.errorLogging(message: "Operator \(self.operatorLabel) aborts calculation cause given invalid data: \(msg)", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+ }
+
+ self.computationDelegate?.operatorWillBeginComputation(self)
+
+ switch computationMode {
+ case .GPU:
+ if !SerranoEngine.configuredEngine.hasAvailableGPU() {
+ SerranoLogging.warningLogging(message: "Serrano Engine has no available configured GPU device. Use CPU doing calculation instead.", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ self.cpu()
+ } else {
+ self.gpu()
+ }
+ case .CPU:
+ self.cpu()
+ case .Auto:
+ // TODO: More intelligent way to decide
+ if self.inputTensors![0].count > 1000000 && SerranoEngine.configuredEngine.hasAvailableGPU(){
+ self.gpu()
+ } else {
+ self.cpu()
+ }
+ }
+ self.computationDelegate?.operatorDidEndComputation(self, outputTensors: self.outputTensors!)
+ }
+
+ /// Compute async
+ ///
+ /// - Parameter computationMode: computationMode
+ public func computeAsync(_ computationMode: OperatorComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.compute(computationMode)
+ }
+ }
+
+
+ /// This operator itself does not do any grad update.
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode description
+ /// - Returns: return value description
+ public func gradCompute(_ computationMode: OperatorComputationMode) -> [String: DataSymbolSupportedDataType] {
+ return [:]
+ }
+
+
+ /// This operator itself does not do any grad update.
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode description
+ /// - upGrads: upGrads description
+ public func gradComputAsync(_ computationMode: OperatorComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.computationDelegate?.operatorWillBeginGradsComputation(self)
+ let result = self.gradCompute(computationMode)
+ self.computationDelegate?.operatorDidEndGradsComputation(self, grads: result)
+ }
+ }
+
+
+ /// Do nothing. No param to update
+ ///
+ /// - Parameters:
+ /// - grads: grads description
+ /// - LR: LR description
+ public func updateParams(grads: [Tensor], LR: Float) {
+ // no param to update
+ return
+ }
+
+ /// This operator has no parameters. Do nothing
+ ///
+ public func bindParamSymbols(_ symbols: [GraphSymbol]) {
+
+ }
+
+ /// This operator returns no param symbols
+ ///
+ /// - Returns: empty array
+ public func paramSymbols() -> [GraphSymbol] {
+ return []
+ }
+
+ /// CPU calculation
+ internal func cpu() {
+ let patchHeight = self.patchSize[0]
+ let patchWidth = self.patchSize[1]
+ let patchElementCount = patchWidth * patchHeight
+
+ let strideHeight = self.stride[0]
+ let strideWidth = self.stride[1]
+
+ let workGroup = DispatchGroup()
+ for (inTensor, outTensor) in zip(self.inputTensors!, self.outputTensors!) {
+ workGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ let inShapeArray = inTensor.shape.shapeArray
+ let (channels, inHeight, inWidth) = parseImgChannelShapeInfo(self.channelPosition, shapeArray: inShapeArray)
+
+ // get out dim size
+ let outHeight = kernelScanningOutSize(self.padMode, inputSize: inHeight, kernelSize: patchHeight, stride: strideHeight)
+ let outWidth = kernelScanningOutSize(self.padMode, inputSize: inWidth, kernelSize: patchWidth, stride: strideWidth)
+
+ let patchCalculationGroup = DispatchGroup()
+ for i in 0.. Img2ColInfo {
+ let (channel, inputHeight, inputWidth) = parseImgChannelShapeInfo(self.channelPosition, shapeArray: inputShape.shapeArray)
+ let kernelScanningXPatchCount = kernelScanningOutSize(self.padMode, inputSize: inputWidth,
+ kernelSize: self.patchSize[1], stride: self.stride[1])
+ return Img2ColInfo(channelPosition: MetalShort(self.channelPosition.rawValue),
+ paddingMode: MetalShort(self.padMode.rawValue), paddingValue: self.paddingValue,
+ channels: MetalInt(channel), inputWidth: MetalInt(inputWidth), inputHeight: MetalInt(inputHeight),
+ kernelScanningXPatchCount: MetalInt(kernelScanningXPatchCount),
+ strideWidth: MetalInt(self.stride[1]), strideHeight: MetalInt(self.stride[0]),
+ patchWdith: MetalInt(self.patchSize[1]), patchHeight: MetalInt(self.patchSize[0]))
+ }
+
+ /// GPU calculation.
+ ///
+ /// Split the output 2D matrix into threadgroups based on each patch and channel index.
+ /// Calculate each element in each group independently.
+ internal func gpu() {
+ // prepare resource
+ let resourcePrepareGroup = DispatchGroup()
+ let engine = SerranoEngine.configuredEngine
+ var kernel: MTLComputePipelineState?
+ var commandBuffers: [MTLCommandBuffer] = [MTLCommandBuffer]()
+ var inputBuffers: [MTLBufferResource] = [MTLBufferResource]()
+ var resultBuffers: [MTLBufferResource] = [MTLBufferResource]()
+
+ //// kernel
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ var info = ""
+ (kernel, info) = engine.loadGPUKernel(kernelLabel: self.metalKernelFuncLabel)
+ guard kernel != nil else {
+ fatalError("[Serrano] Failed to load kernel \(self.metalKernelFuncLabel). Info: \(info)")
+ }
+ resourcePrepareGroup.leave()
+ }
+
+ //// command buffers
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ for _ in 0...stride, at: 2)
+
+ // View output matrix as a [outputHeight * outputWidth, patchCount, channels] matrix, las dim is channels
+ // Each group, spawn thread for each point
+ let outHeight = self.outputTensors![bufferIndex].shape.shapeArray[0]
+ let channels = Int(info.channels)
+ let threadsPerThreadgroup = MTLSizeMake(self.patchSize[1], // width
+ self.patchSize[0], // height
+ 1)
+ let threadgroupsPerGrid = MTLSizeMake(1,
+ outHeight,
+ channels)
+ SerranoLogging.stdLogging(message: "Dispatch group configured with threadgroupsPerGrid: \(threadgroupsPerGrid), threadsPerThreadgroup: \(threadsPerThreadgroup) requested by operator \(self.operatorLabel)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)", loggingLevel: .LowLevel)
+ encoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup)
+
+ encoder.endEncoding()
+
+ buffer.commit()
+ buffer.waitUntilCompleted()
+
+ resourcePrepareGroup.leave()
+ }
+ }
+
+ resourcePrepareGroup.wait()
+ }
+
+
+}
diff --git a/Source/Serrano/operators/nn/misc.swift b/Source/Serrano/operators/nn/misc.swift
new file mode 100644
index 0000000..f643900
--- /dev/null
+++ b/Source/Serrano/operators/nn/misc.swift
@@ -0,0 +1,106 @@
+//
+// misc.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/20/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+
+
+/**
+Padding mode for operators involving with kernel calculation, like pooling and convolution operators.
+We follow same behavior as TensorFlow defined.
+
+## Valid padding
+No padding at all when moving kernel on input tensor.
+
+## Same padding
+Padding to cover all elements
+*/
+public enum PaddingMode: Int {
+ case Valid = 0
+ case Same = 1
+
+ var description: String {
+ get { return String(reflecting: self) }
+ }
+}
+
+
+/// Caculate output size for convolutiona like kernel scanning operation.
+///
+/// ## Valid
+/// `Int(Float((inputSize - stride + 1) / patchSize).rounded(.up))`
+///
+/// ## Same
+/// `Int(Float(inputSize / stride).rounded(.up))`
+///
+/// - Parameters:
+/// - mode: mode
+/// - inputSize: inputSize description
+/// - kernelSize: kernel size
+/// - stride: stride
+/// - Returns: return value
+public func kernelScanningOutSize(_ mode: PaddingMode, inputSize: Int, kernelSize: Int, stride: Int) -> Int {
+ switch mode {
+ case .Same:
+ let val = Float(inputSize) / Float(stride)
+ return Int(val.rounded(.up))
+ case .Valid:
+ let val = Float(inputSize - kernelSize + 1) / Float(stride)
+ return Int(val.rounded(.up))
+ }
+}
+
+
+/**
+The channel order in a N-D tensor.
+
+## First
+Coming before tensor shape.
+For example an image with height `H` and width `W`, it will be represented as [C, H, W]
+
+## Last
+Coming after tensor shape.
+For example an image with height `H` and width `W`, it will be represented as [C, H, W]
+
+*/
+public enum TensorChannelOrder: Int {
+ case First = 0
+ case Last = 1
+
+ var description: String {
+ get { return String(reflecting: self) }
+ }
+}
+
+
+/// According to `channelOrder`, parse `inputShapeArray` to channel, height and width
+///
+/// - Note: rank of `inputShapeArray` should be `3`.
+///
+/// - Parameters:
+/// - channelOrder: channelOrder
+/// - shapeArray: inputShapeArray
+/// - Returns: return value
+public func parseImgChannelShapeInfo(_ channelOrder: TensorChannelOrder, shapeArray: [Int]) -> (channel:Int, height:Int, width: Int) {
+
+ guard shapeArray.count == 3 else {
+ SerranoLogging.errorLogging(message: "Input array should contain 3 element. Given \(shapeArray.count) elements",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+
+ if channelOrder == .First {
+ return (shapeArray[0], shapeArray[1], shapeArray[2])
+ } else if channelOrder == .Last {
+ return (shapeArray[2], shapeArray[0], shapeArray[1])
+ } else {
+ fatalError("Not implemented")
+ }
+}
+
+
+
diff --git a/Source/Serrano/operators/nn/pooling/pooling_op.metal b/Source/Serrano/operators/nn/pooling/pooling_op.metal
new file mode 100644
index 0000000..6f44e09
--- /dev/null
+++ b/Source/Serrano/operators/nn/pooling/pooling_op.metal
@@ -0,0 +1,142 @@
+//
+// pooling_op.metal
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/20/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+#include
+using namespace metal;
+
+typedef struct {
+ short channelPosition; // 0 --> First, 1 --> Last
+ ushort kernelSizeHeight;
+ ushort kernelSizeWidth;
+ ushort strideHeight;
+ ushort strideWidth;
+ uint inHeight;
+ uint inWidth;
+ uint inChannel;
+ uint outHeight;
+ uint outWidth;
+ uint outChannel;
+} Pool2DInfo;
+
+//TODO: Loop unrolling implementation for some specific kernel sizes: 2x2, 3x3, 5x5
+
+void kernel MaxPool2D(constant float* input [[ buffer(0) ]],
+ device float* output [[ buffer(1) ]],
+ constant Pool2DInfo& info [[ buffer(2) ]],
+ uint3 gid [[ thread_position_in_grid ]]) {
+ // check boundary
+ if (gid.x >= info.outWidth || gid.y >= info.outHeight) {
+ return;
+ }
+
+ // get pooling starting index in input
+ uint elementStartIndex = gid.y * info.strideHeight * info.inWidth + gid.x * info.strideWidth + gid.z * info.inWidth * info.inHeight;
+ if (info.channelPosition == 1) { // channel last
+ elementStartIndex = gid.y * info.strideHeight * info.inWidth * info.inChannel + gid.x * info.strideWidth * info.inChannel + gid.z;
+ }
+
+ // valid input boundary
+ int validHeightPoolCount = min(gid.y * info.strideHeight + info.kernelSizeHeight, info.inHeight) - gid.y * info.strideHeight;
+ int validWidthPoolCount = min(gid.x * info.strideWidth + info.kernelSizeWidth, info.inWidth) - gid.x * info.strideWidth;
+
+ // pooling
+ float max_val = -INFINITY;
+ if (info.channelPosition == 0) { // channel first
+ for (int i = 0; i < validHeightPoolCount; i++) {
+ for (int j = 0; j < validWidthPoolCount; j++) {
+ max_val = max(max_val, input[elementStartIndex + i * info.inWidth + j]);
+ }
+ }
+ output[gid.z * info.outHeight * info.outWidth + gid.y * info.outWidth + gid.x] = max_val;
+ } else { // channel last
+ for (int i = 0; i < validHeightPoolCount; i++) {
+ for (int j = 0; j < validWidthPoolCount; j++) {
+ max_val = max(max_val, input[elementStartIndex + i * info.inWidth * info.inChannel + j * info.inChannel]);
+ }
+ }
+ output[gid.y * info.outWidth * info.outChannel + gid.x * info.outChannel + gid.z] = max_val;
+ }
+}
+
+void kernel AvgPool2D (constant float* input [[ buffer(0) ]],
+ device float* output [[ buffer(1) ]],
+ constant Pool2DInfo& info [[ buffer(2) ]],
+ uint3 gid [[ thread_position_in_grid ]]) {
+ // check boundary
+ if (gid.x >= info.outWidth || gid.y >= info.outHeight) {
+ return;
+ }
+
+ // get pooling starting index in input
+ uint elementStartIndex = gid.y * info.strideHeight * info.inWidth + gid.x * info.strideWidth + gid.z * info.inWidth * info.inHeight;
+ if (info.channelPosition == 1) { // channel last
+ elementStartIndex = gid.y * info.strideHeight * info.inWidth * info.inChannel + gid.x * info.strideWidth * info.inChannel + gid.z;
+ }
+
+ // valid input boundary
+ int validHeightPoolCount = min(gid.y * info.strideHeight + info.kernelSizeHeight, info.inHeight) - gid.y * info.strideHeight;
+ int validWidthPoolCount = min(gid.x * info.strideWidth + info.kernelSizeWidth, info.inWidth) - gid.x * info.strideWidth;
+
+ // pooling
+ float sum = 0.0f;
+ if (info.channelPosition == 0) { // channel first
+ for (int i = 0; i < validHeightPoolCount; i++) {
+ for (int j = 0; j < validWidthPoolCount; j++) {
+ sum += input[elementStartIndex + i * info.inWidth + j];
+ }
+ }
+ output[gid.z * info.outHeight * info.outWidth + gid.y * info.outWidth + gid.x] = sum / (info.kernelSizeHeight * info.kernelSizeWidth);
+ } else { // channel last
+ for (int i = 0; i < validHeightPoolCount; i++) {
+ for (int j = 0; j < validWidthPoolCount; j++) {
+ sum += input[elementStartIndex + i * info.inWidth * info.inChannel + j * info.inChannel];
+ }
+ }
+ output[gid.y * info.outWidth * info.outChannel + gid.x * info.outChannel + gid.z] = sum / (info.kernelSizeHeight * info.kernelSizeWidth);
+ }
+
+}
+
+void kernel SumPool2D (constant float* input [[ buffer(0) ]],
+ device float* output [[ buffer(1) ]],
+ constant Pool2DInfo& info [[ buffer(2) ]],
+ uint3 gid [[ thread_position_in_grid ]]) {
+ // check boundary
+ if (gid.x >= info.outWidth || gid.y >= info.outHeight) {
+ return;
+ }
+
+ // get pooling starting index in input
+ uint elementStartIndex = gid.y * info.strideHeight * info.inWidth + gid.x * info.strideWidth + gid.z * info.inWidth * info.inHeight;
+ if (info.channelPosition == 1) { // channel last
+ elementStartIndex = gid.y * info.strideHeight * info.inWidth * info.inChannel + gid.x * info.strideWidth * info.inChannel + gid.z;
+ }
+
+ // valid input boundary
+ int validHeightPoolCount = min(gid.y * info.strideHeight + info.kernelSizeHeight, info.inHeight) - gid.y * info.strideHeight;
+ int validWidthPoolCount = min(gid.x * info.strideWidth + info.kernelSizeWidth, info.inWidth) - gid.x * info.strideWidth;
+
+ // pooling
+ float sum = 0.0f;
+ if (info.channelPosition == 0) { // channel first
+ for (int i = 0; i < validHeightPoolCount; i++) {
+ for (int j = 0; j < validWidthPoolCount; j++) {
+ sum += input[elementStartIndex + i * info.inWidth + j];
+ }
+ }
+ output[gid.z * info.outHeight * info.outWidth + gid.y * info.outWidth + gid.x] = sum;
+ } else { // channel last
+ for (int i = 0; i < validHeightPoolCount; i++) {
+ for (int j = 0; j < validWidthPoolCount; j++) {
+ sum += input[elementStartIndex + i * info.inWidth * info.inChannel + j * info.inChannel];
+ }
+ }
+ output[gid.y * info.outWidth * info.outChannel + gid.x * info.outChannel + gid.z] = sum;
+ }
+
+}
diff --git a/Source/Serrano/operators/nn/pooling/pooling_op.swift b/Source/Serrano/operators/nn/pooling/pooling_op.swift
new file mode 100644
index 0000000..cff2057
--- /dev/null
+++ b/Source/Serrano/operators/nn/pooling/pooling_op.swift
@@ -0,0 +1,698 @@
+//
+// pooling_op.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/20/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import Metal
+import Dispatch
+import Accelerate
+
+
+/// Mirror struct of `Pool2DInfo` in `pooling_op.metal`
+public struct Pool2DInfo {
+ var channelPosition: MetalShort
+ var kernelSizeHeight: MetalUShort
+ var kernelSizeWidth: MetalUShort
+ var strideHeight: MetalUShort
+ var strideWidth: MetalUShort
+ var inHeight: MetalUInt
+ var inWidth: MetalUInt
+ var inChannel: MetalUInt
+ var outHeight: MetalUInt
+ var outWidth: MetalUInt
+ var outChannel: MetalUInt
+
+ /// Generate `Pool2DInfo` from a `Pooling2DOperator`'s information.
+ ///
+ /// - Parameters:
+ /// - op: operator
+ /// - inputSize: input size
+ /// - outputSize: output size
+ /// - Returns: `Pool2DInfo`
+ static func makePool2DInfo(op: Pooling2DOperator, inputSize: [Int], outputSize: [Int]) -> Pool2DInfo {
+ let (inChannel, inHeight, inWidth) = parseImgChannelShapeInfo(op.channelPosition, shapeArray: inputSize)
+ let (outChannel, outHeight, outWidth) = parseImgChannelShapeInfo(op.channelPosition, shapeArray: outputSize)
+ return Pool2DInfo(channelPosition: MetalShort(op.channelPosition.rawValue),
+ kernelSizeHeight: MetalUShort(op.kernelSize[0]), kernelSizeWidth: MetalUShort(op.kernelSize[1]),
+ strideHeight: MetalUShort(op.stride[0]), strideWidth: MetalUShort(op.stride[1]),
+ inHeight: MetalUInt(inHeight), inWidth: MetalUInt(inWidth), inChannel: MetalUInt(inChannel),
+ outHeight: MetalUInt(outHeight), outWidth: MetalUInt(outWidth), outChannel: MetalUInt(outChannel))
+ }
+}
+
+/**
+This class is an abstract class for 2D pooling operators.
+*/
+public class Pooling2DOperator: ComputableOperator {
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Attributes
+
+ /// Operator label. Conforms to `ComputableOperator`
+ public var operatorLabel: String
+
+ /// This operator does not operator on GPU. Conforms to `ComputableOperator`
+ public var metalKernelFuncLabel:String = "" // need override by child class
+
+ /// Conforms to `ComputableOperator`
+ public var computationDelegate: OperatorCalculationDelegate?
+
+ /// Conforms to `ComputableOperator`
+ public var inputTensors: [Tensor]?
+
+ /// Conforms to `ComputableOperator`
+ public var outputTensors: [Tensor]?
+
+ /// Pad mode, default is `PaddingMode.Valid`.
+ public var paddingMode: PaddingMode = PaddingMode.Valid
+
+ /// Kernel size array which contains the kernel size in each dimension.
+ public var kernelSize: [Int]
+
+ /// Stride array which contains the stride in each dimension.
+ public var stride: [Int]
+
+ /// CPU computation block.
+ /// Two tensors are input and output tensors.
+ public lazy var cpuComputeBlock: ((Tensor, Tensor) -> Void)? = nil
+
+ /// Channel position. Default is `ImageChannelOrder.First`
+ public var channelPosition: TensorChannelOrder = .First
+
+ /// If `true`, operator will not call `inputOutputTensorsCheck()` before doing calculation.
+ /// This is used inside framework to speed up in situation we know it will not be wrong.
+ public var disableInputOutputCheck: Bool = false
+
+ /// Indicate if this operator would do paramter update.
+ public var trainable: Bool = false
+
+ /// The mapping type of this operator.
+ /// `OneToOne` for this operator.
+ public var mapType: OperatorMappingType {
+ get {
+ return OperatorMappingType.OneToOne
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Initializers
+
+ /// Initializer.
+ ///
+ /// - Parameters:
+ /// - kernelSize: Array of int values. Should has 2 elemetns for height and width dimesnsions.
+ /// - stride: Array of int values. If `stride` is `nil`, it will be assigned as same value as `kernelSize`
+ /// - channelPosition: channel position in input data
+ /// - paddingMode: paddingMode
+ /// - inputTensors: inputTensors
+ /// - outputTensors: outputTensors
+ /// - computationDelegate: computationDelegate
+ /// - operatorLabel: operatorLabel
+ required public init(kernelSize: [Int],
+ stride: [Int]? = nil,
+ channelPosition: TensorChannelOrder = TensorChannelOrder.First,
+ paddingMode: PaddingMode = PaddingMode.Valid,
+ inputTensors: [Tensor]? = nil,
+ outputTensors: [Tensor]? = nil,
+ computationDelegate: OperatorCalculationDelegate? = nil,
+ operatorLabel: String = "Pooling") {
+ self.kernelSize = kernelSize
+ if stride == nil {
+ self.stride = kernelSize
+ } else {
+ self.stride = stride!
+ }
+ self.channelPosition = channelPosition
+ self.paddingMode = paddingMode
+ self.inputTensors = inputTensors
+ self.outputTensors = outputTensors
+ self.computationDelegate = computationDelegate
+ self.operatorLabel = operatorLabel
+ self.kernelSize = kernelSize
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods
+
+ /// Compute output shape according to `kernelSize`, `stride` and `paddingMode`
+ ///
+ /// - Parameter shapes: shapes description
+ /// - Returns: return value description
+ public func outputShape(shapeArray shapes: [TensorShape]) -> [TensorShape]? {
+ // input not empty
+ guard shapes.count != 0 else {
+ SerranoLogging.errorLogging(message: "Input shapes array is empty",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ // kernel valid
+ guard self.kernelSize.count == 2 && self.kernelSize[0] > 0 && self.kernelSize[1] > 0 else {
+ SerranoLogging.errorLogging(message: "Invalid kernelSize: \(self.kernelSize)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ // stride check
+ guard self.stride.count == 2 && self.stride[0] > 0 && self.stride[0] > 0 else {
+ SerranoLogging.errorLogging(message: "Invalid stride: \(self.stride)",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ var outputShapes = [TensorShape]()
+ for shape in shapes {
+ // check valid shape
+ guard shape.rank == 3 && shape.shapeArray[0] > 0 && shape.shapeArray[1] > 0 && shape.shapeArray[2] > 0 else {
+ SerranoLogging.errorLogging(message: "Input shape is not valid \(shape.description).",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+ let (channel, height, width) = parseImgChannelShapeInfo(self.channelPosition, shapeArray: shape.shapeArray)
+ var outShapeArray = [kernelScanningOutSize(self.paddingMode, inputSize: height,
+ kernelSize: self.kernelSize[0], stride: self.stride[0]),
+ kernelScanningOutSize(self.paddingMode, inputSize: width,
+ kernelSize: self.kernelSize[1], stride: self.stride[1]),
+ channel]
+ if self.channelPosition == TensorChannelOrder.First {
+ outShapeArray = [channel,
+ kernelScanningOutSize(self.paddingMode, inputSize: height,
+ kernelSize: self.kernelSize[0], stride: self.stride[0]),
+ kernelScanningOutSize(self.paddingMode, inputSize: width,
+ kernelSize: self.kernelSize[1], stride: self.stride[1])]
+ }
+
+ // valid out shape
+ guard outShapeArray[0] > 0 && outShapeArray[1] > 0 && outShapeArray[2] > 0 else {
+ SerranoLogging.errorLogging(message: "Input shape \(shape.description) is not valid which will lead to negative output dimension.",
+ file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+ outputShapes.append(TensorShape(dataType: shape.dataType, shape: outShapeArray))
+ }
+
+ return outputShapes
+ }
+
+ /// Check validation between `inputTensors`/`outputTensors` and `stride`, `kernelSize`.
+ ///
+ /// - Returns: check indicating if pass checking, msg for error message.
+ public func inputOutputTensorsCheck() -> (check: Bool, msg: String) {
+ // input not nil
+ guard self.inputTensors != nil else {
+ return (false, "Attribute inputTensors is nil")
+ }
+
+ // output not nil
+ guard self.outputTensors != nil else {
+ return (false, "Attribute outputTensors is nil")
+ }
+
+ // input shape check
+ let inputShapes = self.inputTensors!.map { $0.shape }
+ let outputShapeCheck = self.outputShape(shapeArray: inputShapes)
+ guard outputShapeCheck != nil else {
+ return (false, "Input tensors are not valid. Check log for details.")
+ }
+
+ // output shape check
+ let outputShapes = self.outputTensors!.map { $0.shape }
+ guard outputShapes.count == outputShapeCheck!.count else {
+ return (false, "Attribute outputTensors should have same number of tensors as inputTensors. " +
+ "Expect \(self.inputTensors!.count) tensors, given \(self.outputTensors!.count) tensors.")
+ }
+ for (outputShape, checkShape) in zip(outputShapes, outputShapeCheck!) {
+ guard outputShape == checkShape else {
+ return (false, "One of outputTensors has invalid shape. Expect shape \(checkShape.description), given \(outputShape.description)")
+ }
+ }
+
+ return (true, "")
+ }
+
+
+ /// Compute sync
+ ///
+ /// - Parameter computationMode: computationMode
+ public func compute(_ computationMode: OperatorComputationMode) {
+ // check
+ let (pass, msg) = self.inputOutputTensorsCheck()
+ guard pass else {
+ SerranoLogging.errorLogging(message: "Operator \(self.operatorLabel) aborts calculation cause given invalid data: \(msg)", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ fatalError()
+ }
+
+ self.computationDelegate?.operatorWillBeginComputation(self)
+
+ switch computationMode {
+ case .GPU:
+ if !SerranoEngine.configuredEngine.hasAvailableGPU() {
+ SerranoLogging.warningLogging(message: "Serrano Engine has no available configured GPU device. Use CPU doing calculation instead.", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ self.cpu()
+ } else {
+ self.gpu()
+ }
+ case .CPU:
+ self.cpu()
+ case .Auto:
+ // TODO: More intelligent way to decide
+ if self.inputTensors![0].count > 1000000 && SerranoEngine.configuredEngine.hasAvailableGPU(){
+ self.gpu()
+ } else {
+ self.cpu()
+ }
+ }
+ self.computationDelegate?.operatorDidEndComputation(self, outputTensors: self.outputTensors!)
+ }
+
+
+ /// Compute async
+ ///
+ /// - Parameter computationMode: computationMode
+ public func computeAsync(_ computationMode: OperatorComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.compute(computationMode)
+ }
+ }
+
+ /// Calulate grads sync.
+ /// All unary operator return grads tensor with same number and shape as attribute `inputTensors`.
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ /// - Returns: return grads tensor
+ public func gradCompute(_ computationMode: OperatorComputationMode) -> [String: DataSymbolSupportedDataType] {
+ //TODO: Implementation
+ fatalError("Not implemented")
+ }
+
+ /// Cal grads async
+ ///
+ /// - Parameters:
+ /// - computationMode: computationMode
+ public func gradComputAsync(_ computationMode: OperatorComputationMode) {
+ // check delegate
+ OperatorUtils.delegateNilWarning(op: self, file: "\(#file)", function: "\(#function)", line: #line)
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ _ = self.gradCompute(computationMode)
+ }
+ }
+
+ /// This operator has no parameters. Do nothing
+ ///
+ public func bindParamSymbols(_ symbols: [GraphSymbol]) {
+
+ }
+
+ /// This operator has no parameters.
+ ///
+ /// - Returns: An empty array
+ public func paramSymbols() -> [GraphSymbol] {
+ return [GraphSymbol]()
+ }
+
+ /// CPU calcualtion. Call `cpuComputeBlock` which is defined in subclass
+ internal func cpu() {
+ let workGroup = DispatchGroup()
+ for (input, output) in zip(self.inputTensors!, self.outputTensors!) {
+ workGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.cpuComputeBlock!(input, output)
+ workGroup.leave()
+ }
+ }
+ workGroup.wait()
+ }
+
+ /// GPU calculation
+ internal func gpu() {
+ // prepare resources
+ let resourcePrepareGroup = DispatchGroup()
+ let engine = SerranoEngine.configuredEngine
+ var kernel: MTLComputePipelineState?
+ var commandBuffers: [MTLCommandBuffer] = [MTLCommandBuffer]()
+ var inputBuffers: [MTLBufferResource] = [MTLBufferResource]()
+ var outputBuffers: [MTLBufferResource] = [MTLBufferResource]()
+ var infoBuffers: [MTLBuffer] = [MTLBuffer]()
+
+ // kernel
+ resourcePrepareGroup.enter()
+ DispatchQueue.global(qos: .userInitiated).async {
+ var info = ""
+ (kernel, info) = engine.loadGPUKernel(kernelLabel: self.metalKernelFuncLabel)
+ guard kernel != nil else {
+ fatalError("[Serrano] Failed to load kernel \(self.metalKernelFuncLabel). Info: \(info)")
+ }
+ resourcePrepareGroup.leave()
+ }
+
+ // command buffer
+ for _ in 0...stride,
+ options: MTLResourceOptions.storageModeShared)
+ infoBuffers.append(buffer)
+ resourcePrepareGroup.leave()
+ }
+ }
+
+ resourcePrepareGroup.wait()
+
+ // encoder
+ for index in 0.. Void in
+ let workGroup = DispatchGroup()
+ let outShapeArray = output.shape.shapeArray
+ let inShapeArray = input.shape.shapeArray
+ let stride = self.stride
+ let kernelSize = self.kernelSize
+ let (channel, outHeight, outWidth) = parseImgChannelShapeInfo(self.channelPosition, shapeArray: outShapeArray)
+ let (_, inHeight, inWidth) = parseImgChannelShapeInfo(self.channelPosition, shapeArray: inShapeArray)
+
+ // according to channel order, do pooling.
+ // Here we are trying to taking advantage of spatial locality for input
+ // TODO: I doubt if this really improve performance. Should do profiling and verify.
+ if self.channelPosition == TensorChannelOrder.First {
+ for c in 0.. Void in
+ let workGroup = DispatchGroup()
+ let outShapeArray = output.shape.shapeArray
+ let inShapeArray = input.shape.shapeArray
+ let stride = self.stride
+ let kernelSize = self.kernelSize
+ let (channel, outHeight, outWidth) = parseImgChannelShapeInfo(self.channelPosition, shapeArray: outShapeArray)
+ let (_, inHeight, inWidth) = parseImgChannelShapeInfo(self.channelPosition, shapeArray: inShapeArray)
+
+ // according to channel order, do pooling.
+ // Here we are trying to taking advantage of spatial locality for input
+ // TODO: I doubt if this really improve performance. Should do profiling and verify.
+ if self.channelPosition == TensorChannelOrder.First {
+ for c in 0.. Void in
+ let workGroup = DispatchGroup()
+ let outShapeArray = output.shape.shapeArray
+ let inShapeArray = input.shape.shapeArray
+ let stride = self.stride
+ let kernelSize = self.kernelSize
+ let (channel, outHeight, outWidth) = parseImgChannelShapeInfo(self.channelPosition, shapeArray: outShapeArray)
+ let (_, inHeight, inWidth) = parseImgChannelShapeInfo(self.channelPosition, shapeArray: inShapeArray)
+
+ // according to channel order, do pooling.
+ // Here we are trying to taking advantage of spatial locality for input
+ // TODO: I doubt if this really improve performance. Should do profiling and verify.
+ if self.channelPosition == TensorChannelOrder.First {
+ for c in 0..= 0`.
+ public var learningRate: Float
+
+ /// Initial leanring rate
+ public var initLearningRate: Float {
+ get {
+ return self.initialLR
+ }
+ }
+
+ /// LR Decay method
+ public var decayMethod: LearningRateDecayMethod
+
+ /// Learning rate decay per epoch.
+ /// Before each epoch's parmaeter updating, do `learningRate -= learningRate * decay`.
+ /// Should `>= 0`.
+ public var decay: Float
+
+ /// Momentum.
+ /// Should `>= 0`.
+ public var momentum: Float
+
+ /// Whether to turn on Nesterov momentum.
+ /// Default is `false`.
+ public var nesterov: Bool
+
+ /// Initial leanring rate
+ internal var initialLR: Float
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Init
+
+ public init(learningRate: Float = 0.001, momentum: Float = 0.0,
+ decayMethod: LearningRateDecayMethod = LearningRateDecayMethod.Step, decay: Float = 0.0,
+ nesterov: Bool = false) {
+ self.initialLR = learningRate
+ self.learningRate = learningRate
+ self.momentum = momentum
+ self.decayMethod = decayMethod
+ self.decay = decay
+ self.nesterov = false
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // MARK: - Methods conforming Optimizer protocol
+
+ /// Do some preparing work before each epoch training
+ ///
+ /// - Parameter graph: target graph
+ public func prepare(_ graph: Graph) {
+ // decay learning rate
+ self.learningRate = self.decayMethod.decayLR(initialLR: self.initialLR, decay: self.decay, epoch: graph.epoch)
+ }
+
+ /// Update a data symbol's updated value
+ ///
+ //// - Parameters:
+ /// - dataSymbol: target symbol
+ /// - gradValue: gradvalue fot this time updating
+ public func updateParameter(_ dataSymbol: DataSymbol, gradValue: DataSymbolSupportedDataType) {
+ // momentum
+ if dataSymbol.symbolType == SymbolType.Scalar {
+ var scalarSymbol = dataSymbol as! ScalarSymbol
+ let value: Float = scalarSymbol.bindedData! as! Float
+ let v: Float = self.momentum * scalarSymbol.currentGrad!.scarlarValue - self.learningRate * gradValue.scarlarValue
+ if self.nesterov {
+ scalarSymbol.bindedData! = value + self.momentum * v - self.learningRate * gradValue.scarlarValue
+ } else {
+ scalarSymbol.bindedData! = value + v
+ }
+ } else {
+ var tensorSymbol = dataSymbol as! TensorSymbol
+ let grad = tensorSymbol.currentGrad as! Tensor
+ let v: Tensor = self.momentum * grad &- self.learningRate * gradValue.tensorValue
+ if self.nesterov {
+ // self.learningRate * gradValue.tensorValue cannot use inplace operation. will effect passed-in argument
+ tensorSymbol.bindedData!.tensorValue &+ (self.momentum &* v) &- self.learningRate * gradValue.tensorValue
+ } else {
+ tensorSymbol.bindedData!.tensorValue &+ v
+ }
+ print("\(tensorSymbol.symbolLabel)",grad.flatArrayFloat())
+ }
+ }
+
+
+}
diff --git a/Source/Serrano/utils/logging.swift b/Source/Serrano/utils/logging.swift
new file mode 100644
index 0000000..7320427
--- /dev/null
+++ b/Source/Serrano/utils/logging.swift
@@ -0,0 +1,59 @@
+//
+// logging.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 3/17/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+
+
+public enum SerranoLoggingType{
+ case Regular,
+ MediumLevel,
+ LowLevel
+}
+
+
+public class SerranoLogging {
+
+ public static var enableWarnning = true
+
+ /// If `release` is `true`, all std and warning logging will be omiited.
+ /// Default is `false`.
+ ///
+ public static var release = false
+
+ public static func stdLogging(message: String, file: String, function: String, line: String, loggingLevel: SerranoLoggingType) {
+ if release { return }
+
+ let fileName = file.components(separatedBy: "/").last
+ if fileName != nil {
+ NSLog("[Serrano](File: %@, Function: %@, Line: %@) ==> %@", fileName!.description, function, line, message)
+ } else {
+ NSLog("[Serrano](File: %@, Function: %@, Line: %@) ==> %@", file, function, line, message)
+ }
+ }
+
+ public static func warningLogging(message: String, file: String, function: String, line: String) {
+ if release { return }
+
+ let fileName = file.components(separatedBy: "/").last
+ if fileName != nil {
+ NSLog("[Serrano, Warning⚠️](File: %@, Function: %@, Line: %@) ==> %@", fileName!.description, function, line, message)
+ } else {
+ NSLog("[Serrano, Warning⚠️](File: %@, Function: %@, Line: %@) ==> %@", file, function, line, message)
+ }
+ }
+
+ public static func errorLogging(message: String, file: String, function: String, line: String) {
+ let fileName = file.components(separatedBy: "/").last
+ if fileName != nil {
+ NSLog("[Serrano, ERROR‼️](File: %@, Function: %@, Line: %@) ==> %@", fileName!.description, function, line, message)
+ } else {
+ NSLog("[Serrano, ERROR‼️](File: %@, Function: %@, Line: %@) ==> %@", file, function, line, message)
+ }
+ }
+}
+
diff --git a/Source/Serrano/utils/metal_hardwares.swift b/Source/Serrano/utils/metal_hardwares.swift
new file mode 100644
index 0000000..f87538c
--- /dev/null
+++ b/Source/Serrano/utils/metal_hardwares.swift
@@ -0,0 +1,79 @@
+//
+// metal_hardwares.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 4/22/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import Metal
+
+/// GPU familly not support MetalPerformanceShader
+/// Ref: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
+#if os(iOS)
+public let GPU_FAMILTY_NOT_SUPPORT_MPS = [
+ MTLFeatureSet.iOS_GPUFamily1_v3,
+]
+#endif
+
+/**
+ This util class contains APIs that check the device's hardware capabilities and limits accroding to [Apple's official docs](https://developer.apple.com/metal/availability).
+
+ - Note: The limits values of each type of hardware device should be checked and updated with Apple's [doc](https://developer.apple.com/metal/limits/) frequently. If you find any mismatching with Apple's official doc, please make a PR at github.
+ */
+public class MetalHardwareChecker {
+ /// Maximum length of a data block for a function, per render or compute command encode
+ public static let MAX_BYTES_OF_DATA_BLOCK_PER_KERNEL = 4000
+
+ // Maximum buffer length
+ public static let MAX_BUFFER_SIZE_IN_BYTES = Int(2.56e+8)
+
+
+
+ /// Check if tesor's data size largser thant MAX_BUFFER_SIZE_IN_BYTES
+ ///
+ /// - Parameter tensor: tensor
+ ///
+ /// - Returns:
+ /// - result: If fitting
+ /// - info: Checking infomation
+ public static func tensorSizeFitCheck(tensor: Tensor) -> (result: Bool, info: String) {
+ if tensor.allocatedBytes >= MetalHardwareChecker.MAX_BUFFER_SIZE_IN_BYTES {
+ return (false, "Trying to allocatea MTLBuffer with \(tensor.allocatedBytes) bytes. Metal could not create a MTLBuffer larger than \(MetalHardwareChecker.MAX_BUFFER_SIZE_IN_BYTES) bytes.")
+ } else {
+ return (true, "")
+ }
+ }
+
+
+ /// If current GPU support MetalPerformanceShaser
+ ///
+ /// - Returns: result bool
+ public static func supportMPS() -> Bool {
+ // macos test
+ #if os(OSX)
+ if #available(OSX 10.13, *) {
+ return true
+ } else {
+ return false
+ }
+ #endif
+
+ // ios Test
+ #if os(iOS)
+ let device = SerranoEngine.configuredEngine.GPUDevice
+ guard device != nil else {
+ return false
+ }
+
+ for feature in GPU_FAMILTY_NOT_SUPPORT_MPS {
+ if device!.supportsFeatureSet(feature) {
+ return false
+ }
+ }
+
+ return true
+ #endif
+ }
+}
diff --git a/Source/Serrano/utils/type_ext.swift b/Source/Serrano/utils/type_ext.swift
new file mode 100644
index 0000000..584bdda
--- /dev/null
+++ b/Source/Serrano/utils/type_ext.swift
@@ -0,0 +1,165 @@
+//
+// type_ext.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 4/7/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///
+
+extension Int {
+
+ /// Description
+ ///
+ /// - Parameter size: size description
+ /// - Returns: return value description
+ func padded(alignmentSize size: Int) -> Int? {
+ guard self >= 0 && size > 0 else {
+ SerranoLogging.warningLogging(message: "Undefined padding action from \(self) to \(size).", file: "\(#file)", function: "\(#function)", line: "\(#line)")
+ return nil
+ }
+
+ let remainder = self % size
+ if remainder == 0 {
+ return self
+ } else {
+ return self + size - remainder
+ }
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+public protocol SupportedScalarDataType {
+
+ var floatValue: Float {get}
+
+}
+
+extension Int: SupportedScalarDataType {
+
+
+ public var floatValue: Float {
+ return Float(self)
+ }
+
+}
+
+extension Double: SupportedScalarDataType {
+
+ public var floatValue: Float {
+ return Float(self)
+ }
+}
+
+extension Float: SupportedScalarDataType {
+
+
+ public var floatValue: Float {
+ return self
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+public protocol SupportedNestedType {}
+extension Array: SupportedNestedType {}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// In Metal, UInt is 32 bits. In iOS, it is 64 bits on 64 bit system.
+typealias MetalUInt = UInt32
+
+typealias MetalUShort = UInt16
+
+typealias MetalShort = Int16
+
+typealias MetalInt = Int32
+
+typealias MetalFloat = Float32
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+public protocol DataSymbolSupportedDataType {
+ var description: String {get}
+ var tensorValue: Tensor {get}
+ var scarlarValue: Float {get}
+}
+extension Int: DataSymbolSupportedDataType {
+ public var description: String {
+ get {
+ return "Int value: \(self)"
+ }
+ }
+
+ public var tensorValue: Tensor {
+ get {
+ fatalError("This DataSymbolSupportedDataType is a scarlar.")
+ }
+ }
+
+ public var scarlarValue: Float {
+ get {
+ return Float(self)
+ }
+ }
+}
+extension Double: DataSymbolSupportedDataType {
+ public var description:String {
+ get {
+ return "Double value: \(self)"
+ }
+ }
+
+ public var tensorValue: Tensor {
+ get {
+ fatalError("This DataSymbolSupportedDataType is a scarlar.")
+ }
+ }
+
+ public var scarlarValue: Float {
+ get {
+ return Float(self)
+ }
+ }
+}
+extension Float: DataSymbolSupportedDataType {
+ public var description:String {
+ get {
+ return "Float value: \(self)"
+ }
+ }
+
+ public var tensorValue: Tensor {
+ get {
+ fatalError("This DataSymbolSupportedDataType is a scarlar.")
+ }
+ }
+
+ public var scarlarValue: Float {
+ get {
+ return self
+ }
+ }
+}
+extension Tensor: DataSymbolSupportedDataType {
+ public var scarlarValue: Float {
+ get {
+ fatalError("This DataSymbolSupportedDataType is a tensor.")
+ }
+ }
+
+ public var tensorValue: Tensor {
+ get {
+ return self
+ }
+ }
+}
+
+
diff --git a/Source/Serrano/utils/utils.swift b/Source/Serrano/utils/utils.swift
new file mode 100644
index 0000000..c8c7bee
--- /dev/null
+++ b/Source/Serrano/utils/utils.swift
@@ -0,0 +1,40 @@
+//
+// utils.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 3/9/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Foundation
+import Metal
+import Accelerate
+
+
+
+/**
+ Get `CBLAS_TRANSPOSE` enum value from Bool marker
+ */
+public func cblasTrans(_ mark: Bool) -> CBLAS_TRANSPOSE {
+ if mark {
+ return CblasTrans
+ }
+ else {
+ return CblasNoTrans
+ }
+}
+
+
+/**
+Weak reference object. Used in container of weak reference
+*/
+public class WeakRef {
+ weak var value : T?
+ init (value: T) {
+ self.value = value
+ }
+}
+
+
+
+
diff --git a/Source/SupportingFiles/Info.plist b/Source/SupportingFiles/Info.plist
new file mode 100644
index 0000000..fada70e
--- /dev/null
+++ b/Source/SupportingFiles/Info.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ Serrano
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ $(CURRENT_PROJECT_VERSION)
+ NSPrincipalClass
+
+
+
diff --git a/Source/SupportingFiles/Serrano.h b/Source/SupportingFiles/Serrano.h
new file mode 100644
index 0000000..fd213ea
--- /dev/null
+++ b/Source/SupportingFiles/Serrano.h
@@ -0,0 +1,19 @@
+//
+// Serrano.h
+// Serrano
+//
+// Created by ZHONGHAO LIU on 11/1/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+#import
+
+//! Project version number for Serrano.
+FOUNDATION_EXPORT double SerranoVersionNumber;
+
+//! Project version string for Serrano.
+FOUNDATION_EXPORT const unsigned char SerranoVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import
+
+
diff --git a/Source/library/FBSUtil/FBSUtil.c b/Source/library/FBSUtil/FBSUtil.c
new file mode 100644
index 0000000..e116432
--- /dev/null
+++ b/Source/library/FBSUtil/FBSUtil.c
@@ -0,0 +1,13 @@
+//
+// FBSUtil.c
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/5/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+#include "FBSUtil.h"
+
+void hello() {
+ printf("\n\nHello world\n\n");
+}
diff --git a/Source/library/FBSUtil/FBSUtil.h b/Source/library/FBSUtil/FBSUtil.h
new file mode 100644
index 0000000..e1c8aeb
--- /dev/null
+++ b/Source/library/FBSUtil/FBSUtil.h
@@ -0,0 +1,16 @@
+//
+// FBSUtil.h
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/5/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+#ifndef FBSUtil_h
+#define FBSUtil_h
+
+#include
+
+void hello();
+
+#endif /* FBSUtil_h */
diff --git a/Source/library/FBSUtil/README.md b/Source/library/FBSUtil/README.md
new file mode 100644
index 0000000..fe0ddb1
--- /dev/null
+++ b/Source/library/FBSUtil/README.md
@@ -0,0 +1,4 @@
+# C Library
+This folder contains C modules used in Serrano.
+
+- flatbuffers
\ No newline at end of file
diff --git a/Source/library/FBSUtil/flatcc/LICENSE b/Source/library/FBSUtil/flatcc/LICENSE
new file mode 100644
index 0000000..48b3016
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2015 Mikkel F. Jørgensen, dvide.com
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/Source/library/FBSUtil/flatcc/README.md b/Source/library/FBSUtil/flatcc/README.md
new file mode 100644
index 0000000..fb6c79f
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/README.md
@@ -0,0 +1,4 @@
+# Flatcc codebase
+Clip from [dvidelabs/flatcc](https://github.com/dvidelabs/flatcc)
+
+Copyright 2015 Mikkel F. Jørgensen, dvide.com
\ No newline at end of file
diff --git a/Source/library/FBSUtil/flatcc/flatcc.h b/Source/library/FBSUtil/flatcc/flatcc.h
new file mode 100644
index 0000000..4f69766
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/flatcc.h
@@ -0,0 +1,255 @@
+#ifndef FLATCC_H
+#define FLATCC_H
+
+/*
+ * This is the primary `flatcc` interface when compiling `flatcc` as a
+ * library. Functions and types in the this interface will be kept
+ * stable to the extend possible or reasonable, but do not rely on other
+ * interfaces except "config.h" used to set default options for this
+ * interface.
+ *
+ * This interface is unrelated to the standalone flatbuilder library
+ * which has a life of its own.
+ */
+
+#ifndef UINT8_MAX
+#include
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4820) /* x bytes padding added in struct */
+#endif
+
+typedef struct flatcc_options flatcc_options_t;
+typedef void (*flatcc_error_fun) (void *err_ctx, const char *buf, int len);
+
+struct flatcc_options {
+ size_t max_schema_size;
+ int max_include_depth;
+ int max_include_count;
+ int disable_includes;
+ int allow_boolean_conversion;
+ int allow_enum_key;
+ int allow_enum_struct_field;
+ int allow_multiple_key_fields;
+ int allow_scan_for_all_fields;
+ int allow_string_key;
+ int allow_struct_field_deprecate;
+ int allow_struct_field_key;
+ int allow_struct_root;
+ int ascending_enum;
+ int hide_later_enum;
+ int hide_later_struct;
+ int offset_size;
+ int voffset_size;
+ int utype_size;
+ int bool_size;
+ int require_root_type;
+ int strict_enum_init;
+ uint64_t vt_max_count;
+
+ const char *default_schema_ext;
+ const char *default_bin_schema_ext;
+ const char *default_bin_ext;
+
+ /* Code Generator specific options. */
+ int gen_stdout;
+ int gen_dep;
+
+ const char *gen_depfile;
+ const char *gen_deptarget;
+ const char *gen_outfile;
+
+ int gen_append;
+
+ int cgen_pad;
+ int cgen_sort;
+ int cgen_pragmas;
+
+ int cgen_common_reader;
+ int cgen_common_builder;
+ int cgen_reader;
+ int cgen_builder;
+ int cgen_verifier;
+ int cgen_json_parser;
+ int cgen_json_printer;
+ int cgen_recursive;
+ int cgen_spacing;
+
+ int bgen_bfbs;
+ int bgen_qualify_names;
+ int bgen_length_prefix;
+
+ /* Namespace args - these can override defaults so are null by default. */
+ const char *ns;
+ const char *nsc;
+
+ const char **inpaths;
+ const char **srcpaths;
+ int inpath_count;
+ int srcpath_count;
+ const char *outpath;
+};
+
+/* Runtime configurable optoins. */
+void flatcc_init_options(flatcc_options_t *opts);
+
+typedef void *flatcc_context_t;
+
+/*
+ * Call functions below in order listed one at a time.
+ * Each parse requires a new context.
+ *
+ * A reader file is named after the source base name, e.g.
+ * `monster.fbs` becomes `monster.h`. Builders are optional and created
+ * as `monster_builder.h`. A reader require a common header
+ * `flatbuffers_commoner.h` and a builder requires
+ * `flatbuffers_common_builder.h` in addition to the reader filers. A
+ * reader need no other source, but builders must link with the
+ * `flatbuilder` library and include files in `include/flatbuffers`.
+ *
+ * All the files may also be concatenated into one single file and then
+ * files will not be attempted included externally. This can be used
+ * with stdout output. The common builder can follow the common
+ * reader immediately, or at any later point before the first builder.
+ * The common files should only be included once, but not harm is done
+ * if duplication occurs.
+ *
+ * The outpath is prefixed every output filename. The containing
+ * directory must exist, but the prefix may have text following
+ * the directory, for example the namespace. If outpath = "stdout",
+ * files are generated to stdout.
+ *
+ * Note that const char * options must remain valid for the lifetime
+ * of the context since they are not copied. The options object itself
+ * is not used after initialization and may be reused.
+*/
+
+/*
+ * `name` is the name of the schema file or buffer. If it is path, the
+ * basename is extracted (leading path stripped), and the default schema
+ * extension is stripped if present. The resulting name is used
+ * internally when generating output files. Typically the `name`
+ * argument will be the same as a schema file path given to
+ * `flatcc_parse_file`, but it does not have to be.
+ *
+ * `name` may be null if only common files are generated.
+ *
+ * `error_out` is an optional error handler. If null output is truncated
+ * to a reasonable size and sent to stderr. `error_ctx` is provided as
+ * first argument to `error_out` if `error_out` is non-zero, otherwise
+ * it is ignored.
+ *
+ * Returns context or null on error.
+ */
+flatcc_context_t flatcc_create_context(flatcc_options_t *options, const char *name,
+ flatcc_error_fun error_out, void *error_ctx);
+
+/* Like `flatcc_create_context`, but with length argument for name. */
+/*
+ * Parse is optional - not needed for common files. If the input buffer version
+ * is called, the buffer must be zero terminated, otherwise an input
+ * path can be specified. The output path can be null.
+ *
+ * Only one parse can be called per context.
+ *
+ * The buffer size is limited to the max_schema_size option unless it is
+ * 0. The default is reasonable size like 64K depending on config flags.
+ *
+ * The buffer must remain valid for the duration of the context.
+ *
+ * The schema cannot contain include statements when parsed as a buffer.
+ *
+ * Returns 0 on success.
+ */
+int flatcc_parse_buffer(flatcc_context_t ctx, const char *buf, size_t buflen);
+
+/*
+ * If options contain a non-zero `inpath` option, the resulting filename is
+ * prefixed with that path unless the filename is an absolute path.
+ *
+ * Errors are sent to the error handler given during initialization,
+ * or to stderr.
+ *
+ * The file size is limited to the max_schema_size option unless it is
+ * 0. The default is reasonable size like 64K depending on config flags.
+ *
+ * Returns 0 on success.
+ */
+int flatcc_parse_file(flatcc_context_t ctx, const char *filename);
+
+/*
+ * Generate output files. The basename derived when the context was
+ * created is used used to name the output files with respective
+ * extensions. If the outpath option is not null it is prefixed the
+ * output files. The `cgen_common_reader, cgen_common_builder,
+ * cgen_reader, and cgen_builder` must be set or reset depending on what
+ * is to be generated. The common files do not require a parse, and the
+ * non-common files require a successfull parse or the result is
+ * undefined.
+ *
+ * Unlinke the parser, the code generator produce errors to stderr
+ * always. These errors are rare, such as using too long namespace
+ * names.
+ *
+ * If the `gen_stdout` option is set, all files are generated to stdout.
+ * In this case it is unwise to mix C and binary schema output options.
+ *
+ * If `bgen_bfbs` is set, a binary schema is generated to a file with
+ * the `.bfbs` extension. See also `flatcc_generate_binary_schema` for
+ * further details. Only `flatcc_generate_files` is called via the
+ * `flatcc` cli command.
+ *
+ * The option `bgen_length_prefix` option will cause a length prefix to be
+ * written to the each output binary schema. This option is only
+ * understood when writing to files.
+ *
+ * Returns 0 on success.
+ */
+int flatcc_generate_files(flatcc_context_t ctx);
+
+/*
+ * Returns a buffer with a binary schema for a previous parse.
+ * The user is responsible for calling `free` on the returned buffer
+ * unless it returns 0 on error.
+ *
+ * Can be called instead of generate files, before, or after, but a
+ * schema must be parsed first.
+ *
+ * Returns a binary schema in `reflection.fbs` format. Any included
+ * files will be contained in the schema and there are no separate
+ * schema files for included schema.
+ *
+ * All type names are scoped, mening that they are refixed their
+ * namespace using `.` as the namespace separator, for example:
+ * "MyGame.Example.Monster". Note that the this differs from the current
+ * `flatc` compiler which does not prefix names. Enum names are not
+ * scoped, but the scope is implied by the containing enum type.
+ * The option `bgen_qualify_names=0` changes this behavior.
+ *
+ * If the default option `ascending_enum` is disabled, the `flatcc` will
+ * accept duplicate values and overlapping ranges like the C programming
+ * language. In this case enum values in the binary schema will not be
+ * searchable. At any rate enum names are not searchable in the current
+ * schema format.
+ *
+ */
+void *flatcc_generate_binary_schema(flatcc_context_t ctx, size_t *size);
+
+/*
+ * Similar to `flatcc_generate_binary_schema` but copies the binary
+ * schema into a user supplied buffer. If the buffer is too small
+ * the return value will be negative and the buffer content undefined.
+ */
+int flatcc_generate_binary_schema_to_buffer(flatcc_context_t ctx, void *buf, size_t bufsiz);
+
+/* Must be called to deallocate resources eventually - it valid but
+ * without effect to call with a null context. */
+void flatcc_destroy_context(flatcc_context_t ctx);
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#endif /* FLATCC_H */
diff --git a/Source/library/FBSUtil/flatcc/flatcc_accessors.h b/Source/library/FBSUtil/flatcc/flatcc_accessors.h
new file mode 100644
index 0000000..41d5017
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/flatcc_accessors.h
@@ -0,0 +1,89 @@
+#ifndef FLATCC_ACCESSORS
+#define FLATCC_ACCESSORS
+
+#ifndef UINT8_MAX
+#include
+#endif
+
+#define __flatcc_basic_scalar_accessors_impl(N, T, W, E) \
+static inline size_t N ## __size() \
+{ return sizeof(T); } \
+static inline T *N ## __ptr_add(T *p, size_t i) \
+{ return p + i; } \
+static inline const T *N ## __const_ptr_add(const T *p, size_t i) \
+{ return p + i; } \
+static inline T N ## _read_from_pe(const void *p) \
+{ return N ## _cast_from_pe(*(T *)p); } \
+static inline T N ## _read_to_pe(const void *p) \
+{ return N ## _cast_to_pe(*(T *)p); } \
+static inline T N ## _read(const void *p) \
+{ return *(T *)p; } \
+static inline void N ## _write_from_pe(void *p, T v) \
+{ *(T *)p = N ## _cast_from_pe(v); } \
+static inline void N ## _write_to_pe(void *p, T v) \
+{ *(T *)p = N ## _cast_to_pe(v); } \
+static inline void N ## _write(void *p, T v) \
+{ *(T *)p = v; }
+
+#define __flatcc_define_integer_accessors_impl(N, T, W, E) \
+static inline T N ## _cast_from_pe(T v) \
+{ return (T) E ## W ## toh((uint ## W ## _t)v); } \
+static inline T N ## _cast_to_pe(T v) \
+{ return (T) hto ## E ## W((uint ## W ## _t)v); } \
+static inline T N ## _cast_from_le(T v) \
+{ return (T) le ## W ## toh((uint ## W ## _t)v); } \
+static inline T N ## _cast_to_le(T v) \
+{ return (T) htole ## W((uint ## W ## _t)v); } \
+static inline T N ## _cast_from_be(T v) \
+{ return (T) be ## W ## toh((uint ## W ## _t)v); } \
+static inline T N ## _cast_to_be(T v) \
+{ return (T) htobe ## W((uint ## W ## _t)v); } \
+__flatcc_basic_scalar_accessors_impl(N, T, W, E)
+
+#define __flatcc_define_real_accessors_impl(N, T, W, E) \
+union __ ## N ## _cast { T v; uint ## W ## _t u; }; \
+static inline T N ## _cast_from_pe(T v) \
+{ union __ ## N ## _cast x; \
+ x.v = v; x.u = E ## W ## toh(x.u); return x.v; } \
+static inline T N ## _cast_to_pe(T v) \
+{ union __ ## N ## _cast x; \
+ x.v = v; x.u = hto ## E ## W(x.u); return x.v; } \
+static inline T N ## _cast_from_le(T v) \
+{ union __ ## N ## _cast x; \
+ x.v = v; x.u = le ## W ## toh(x.u); return x.v; } \
+static inline T N ## _cast_to_le(T v) \
+{ union __ ## N ## _cast x; \
+ x.v = v; x.u = htole ## W(x.u); return x.v; } \
+static inline T N ## _cast_from_be(T v) \
+{ union __ ## N ## _cast x; \
+ x.v = v; x.u = be ## W ## toh(x.u); return x.v; } \
+static inline T N ## _cast_to_be(T v) \
+{ union __ ## N ## _cast x; \
+ x.v = v; x.u = htobe ## W(x.u); return x.v; } \
+__flatcc_basic_scalar_accessors_impl(N, T, W, E)
+
+#define __flatcc_define_integer_accessors(N, T, W, E) \
+__flatcc_define_integer_accessors_impl(N, T, W, E)
+
+#define __flatcc_define_real_accessors(N, T, W, E) \
+__flatcc_define_real_accessors_impl(N, T, W, E)
+
+#define __flatcc_define_basic_integer_accessors(NS, TN, T, W, E) \
+__flatcc_define_integer_accessors(NS ## TN, T, W, E)
+
+#define __flatcc_define_basic_real_accessors(NS, TN, T, W, E) \
+__flatcc_define_real_accessors(NS ## TN, T, W, E)
+
+#define __flatcc_define_basic_scalar_accessors(NS, E) \
+__flatcc_define_basic_integer_accessors(NS, uint8, uint8_t, 8, E) \
+__flatcc_define_basic_integer_accessors(NS, uint16, uint16_t, 16, E) \
+__flatcc_define_basic_integer_accessors(NS, uint32, uint32_t, 32, E) \
+__flatcc_define_basic_integer_accessors(NS, uint64, uint64_t, 64, E) \
+__flatcc_define_basic_integer_accessors(NS, int8, int8_t, 8, E) \
+__flatcc_define_basic_integer_accessors(NS, int16, int16_t, 16, E) \
+__flatcc_define_basic_integer_accessors(NS, int32, int32_t, 32, E) \
+__flatcc_define_basic_integer_accessors(NS, int64, int64_t, 64, E) \
+__flatcc_define_basic_real_accessors(NS, float, float, 32, E) \
+__flatcc_define_basic_real_accessors(NS, double, double, 64, E)
+
+#endif /* FLATCC_ACCESSORS */
diff --git a/Source/library/FBSUtil/flatcc/flatcc_builder.h b/Source/library/FBSUtil/flatcc/flatcc_builder.h
new file mode 100644
index 0000000..1de4a1f
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/flatcc_builder.h
@@ -0,0 +1,1576 @@
+#ifndef FLATCC_BUILDER_H
+#define FLATCC_BUILDER_H
+
+/**
+ * Library for building untyped FlatBuffers. Intended as a support
+ * library for generated C code to produce typed builders, but might
+ * also be useful in runtime environments and as support for scripting
+ * languages.
+ *
+ * The builder has two API layers: a stack based `start/end` approach,
+ * and a direct `create`, and they may be fixed freely. The direct
+ * appraoch may be used as part of more specialized optimizations such
+ * as rewriting buffers while the stack approach is convenient for state
+ * machine driven parsers without a stack, or with a very simple stack
+ * without extra allocations.
+ *
+ * The builder emits partial buffer sequences to a user provided emitter
+ * function and does not require a full buffer reprensenation in memory.
+ * For this reason it also does not support sorting or other operations
+ * that requires representing the buffer, but post-processors can easily
+ * do this, and the generated schema specific code and provide functions
+ * to handle this.
+ *
+ * A custom allocator with a default realloc implementation can place
+ * restraints on resource consumption and provide initial allocation
+ * sizes for various buffers and stacks in use.
+ *
+ * A buffer under construction uses a virtual address space for the
+ * completed part of the buffer, starting at 0 and growing in both
+ * directions, or just down depending on whether vtables should be
+ * clustered at the end or not. Clustering may help caching and
+ * preshipping that part of the buffer.
+ *
+ * Because an offset cannot be known before its reference location is
+ * defined, every completed table, vector, etc. returns a reference into
+ * the virtual address range. If the final buffer keeps the 0 offset,
+ * these references remain stable an may be used for external references
+ * into the buffer.
+ *
+ * The maximum buffer than can be constructed is in praxis limited to
+ * half the UOFFSET_MAX size, typically 2^31 bytes, not counting
+ * clustered vtables that may consume and additional 2^31 bytes
+ * (positive address range), but in praxis cannot because vtable
+ * references are signed and thus limited to 2^31 bytes (or equivalent
+ * depending on the flatbuffer types chosen).
+ *
+ * CORRECTION: in various places rules are mentioned about nesting and using
+ * a reference at most once. In fact, DAG's are also valid flatbuffers.
+ * This means a reference may be reused as long as each individual use
+ * obeys the rules and, for example, circular references are not
+ * constructed (circular types are ok, but objects graphs with cycles
+ * are not permitted). Be especially aware of the offset vector create
+ * call which translates the references into offsets - this can be
+ * reverted by noting the reference in vector and calculate the base
+ * used for the offset to restore the original references after the
+ * vector has been emitted.
+ */
+
+#include
+#ifndef UINT8_MAX
+#include
+#endif
+
+#include "flatcc_flatbuffers.h"
+#include "flatcc_emitter.h"
+
+/* It is possible to enable logging here. */
+#ifndef FLATCC_BUILDER_ASSERT
+#define FLATCC_BUILDER_ASSERT(cond, reason) assert(cond)
+#endif
+
+/*
+ * Eror handling is not convenient and correct use should not cause
+ * errors beyond possibly memory allocation, but assertions are a
+ * good way to trace problems.
+ *
+ * Note: some internal assertion will remain if disabled.
+ */
+#ifndef FLATCC_BUILDER_ASSERT_ON_ERROR
+#define FLATCC_BUILDER_ASSERT_ON_ERROR 1
+#endif
+
+/*
+ * If set, checks user input agains state and returns error,
+ * otherwise errors are ignored (assuming they won't happen).
+ * Errors will be asserted if enabled and checks are not skipped.
+ */
+#ifndef FLATCC_BUILDER_SKIP_CHECKS
+#define FLATCC_BUILDER_SKIP_CHECKS 0
+#endif
+
+
+/*
+ * When adding the same field to a table twice this is either an error
+ * or the existing field is returned, potentially introducing garbage
+ * if the type is a vector, table, or string. When implementing parsers
+ * it may be convenient to not treat this as an error.
+ */
+#ifndef FLATCC_BUILDER_ALLOW_REPEAT_TABLE_ADD
+#define FLATCC_BUILDER_ALLOW_REPEAT_TABLE_ADD 0
+#endif
+
+/**
+ * This type must have same size as `flatbuffers_uoffset_t`
+ * and must be a signed type.
+ */
+typedef flatbuffers_soffset_t flatcc_builder_ref_t;
+
+/**
+ * Virtual tables are off by one to avoid being mistaken for error at
+ * position 0, and it makes them detectable as such because no other
+ * reference is uneven. Vtables are emitted at their actual location
+ * which is one less than the reference value.
+ */
+typedef flatbuffers_soffset_t flatcc_builder_vt_ref_t;
+
+typedef flatbuffers_uoffset_t flatcc_builder_identifier_t;
+
+/**
+ * Hints to custom allocators so they can provide initial alloc sizes
+ * etc. There will be at most one buffer for each allocation type per
+ * flatcc_builder instance. Buffers containing only structs may avoid
+ * allocation altogether using a `create` call. The vs stack must hold
+ * vtable entries for all open tables up to their requested max id, but
+ * unused max id overlap on the stack. The final vtables only store the
+ * largest id actually added. The fs stack must hold stack frames for
+ * the nesting levels expected in the buffer, each about 50-100 bytes.
+ * The ds stack holds open vectors, table data, and nested buffer state.
+ * `create` calls bypass the `ds` and `fs` stack and are thus faster.
+ * The vb buffer holds a copy of all vtables seen and emitted since last
+ * vtable flush. The patch log holds a uoffset for every table field
+ * added to currently open tables. The hash table holds a uoffset entry
+ * for each hash slot where the allocator decides how many to provide
+ * above a certain minimum. The vd buffer allocates vtable descriptors
+ * which is a reference to an emitted vtable, an offset to a cached
+ * vtable, and a link to next descriptor with same hash. Calling `reset`
+ * after build can either keep the allocation levels for the next
+ * buffer, or reduce the buffers already allocated by requesting 1 byte
+ * allocations (meaning provide a default).
+ *
+ * The user stack is not automatically allocated, but when entered
+ * explicitly, the boundary is rembered in the current live
+ * frame.
+ */
+enum flatcc_builder_alloc_type {
+ /* The stack where vtables are build. */
+ flatcc_builder_alloc_vs,
+ /* The stack where data structures are build. */
+ flatcc_builder_alloc_ds,
+ /* The virtual table buffer cache, holds a copy of each vt seen. */
+ flatcc_builder_alloc_vb,
+ /* The patch log, remembers table fields with outstanding offset refs. */
+ flatcc_builder_alloc_pl,
+ /* The stack of frames for nested types. */
+ flatcc_builder_alloc_fs,
+ /* The hash table part of the virtual table cache. */
+ flatcc_builder_alloc_ht,
+ /* The vtable descriptor buffer, i.e. list elements for emitted vtables. */
+ flatcc_builder_alloc_vd,
+ /* User stack frame for custom data. */
+ flatcc_builder_alloc_us,
+
+ /* Number of allocation buffers. */
+ flatcc_builder_alloc_buffer_count
+};
+
+/** Must reflect the `flatcc_builder_alloc_type` enum. */
+#define FLATCC_BUILDER_ALLOC_BUFFER_COUNT flatcc_builder_alloc_buffer_count
+
+/**
+ * Emits data to a conceptual deque by appending to either front or
+ * back, starting from offset 0.
+ *
+ * Each emit call appends a strictly later or earlier sequence than the
+ * last emit with same offset sign. Thus a buffer is gradually grown at
+ * both ends. `len` is the combined length of all iov entries such that
+ * `offset + len` yields the former offset for negative offsets and
+ * `offset + len` yields the next offset for non-negative offsets.
+ * The bulk of the data will be in the negative range, possibly all of
+ * it. The first emitted emitted range will either start or end at
+ * offset 0. If offset 0 is emitted, it indicates the start of clustered
+ * vtables. The last positive (non-zero) offset may be zero padding to
+ * place the buffer in a full multiple of `block_align`, if set.
+ *
+ * No iov entry is empty, 0 < iov_count <= FLATCC_IOV_COUNT_MAX.
+ *
+ * The source data are in general ephemeral and should be consumed
+ * immediately, as opposed to caching iov.
+ *
+ * For high performance applications:
+ *
+ * The `create` calls may reference longer living data, but header
+ * fields etc. will still be short lived. If an emitter wants to
+ * reference data in another buffer rather than copying, it should
+ * inspect the memory range. The length of an iov entry may also be used
+ * since headers are never very long (anything starting at 16 bytes can
+ * safely be assumed to be user provided, or static zero padding). It is
+ * guaranteed that data pointers in `create` calls receive a unique slot
+ * separate from temporary headers, in the iov table which may be used
+ * for range checking or hashing (`create_table` is the only call that
+ * mutates the data buffer). It is also guaranteed (with the exception
+ * of `create_table` and `create_cached_vtable`) that data provided to
+ * create calls are not referenced at all by the builder, and these data
+ * may therefore de-facto be handles rather than direct pointers when
+ * the emitter and data provider can agree on such a protocol. This does
+ * NOT apply to any start/end/add/etc. calls which do copy to stack.
+ * `flatcc_builder_padding_base` may be used to test if an iov entry is
+ * zero padding which always begins at that address.
+ *
+ * Future: the emit interface could be extended with a type code
+ * and return an existing object insted of the emitted if, for
+ * example, they are identical. Outside this api level, generated
+ * code could provide a table comparison function to help such
+ * deduplication. It would be optional because two equal objects
+ * are not necessarily identical. The emitter already receives
+ * one object at time.
+ *
+ * Returns 0 on success and otherwise causes the flatcc_builder
+ * to fail.
+ */
+typedef int flatcc_builder_emit_fun(void *emit_context,
+ const flatcc_iovec_t *iov, int iov_count, flatbuffers_soffset_t offset, size_t len);
+
+/*
+ * Returns a pointer to static padding used in emitter calls. May
+ * sometimes also be used for empty defaults such as identifier.
+ */
+extern const uint8_t flatcc_builder_padding_base[];
+
+/**
+ * `request` is a minimum size to be returned, but allocation is
+ * expected to grow exponentially or in reasonable chunks. Notably,
+ * `alloc_type = flatcc_builder_alloc_ht` will only use highest available
+ * power of 2. The allocator may shrink if `request` is well below
+ * current size but should avoid repeated resizing on small changes in
+ * request sizes. If `zero_fill` is non-zero, allocated data beyond
+ * the current size must be zeroed. The buffer `b` may be null with 0
+ * length initially. `alloc_context` is completely implementation
+ * dependendent, and not needed when just relying on realloc. The
+ * resulting buffer may be the same or different with moved data, like
+ * realloc. Returns -1 with unmodified buffer on failure or 0 on
+ * success. The `alloc_type` identifies the buffer type. This may be
+ * used to cache buffers between instances of builders, or to decide a
+ * default allocation size larger than requested. If `need` is zero the
+ * buffer should be deallocate if non-zero, and return success (0)
+ * regardless.
+ */
+typedef int flatcc_builder_alloc_fun(void *alloc_context,
+ flatcc_iovec_t *b, size_t request, int zero_fill, int alloc_type);
+
+/*
+ * The number of hash slots there will be allocated space for. The
+ * allocator may provide more. The size returned should be
+ * `sizeof(flatbuffers_uoffset_t) * count`, where the size is a power of
+ * 2 (or the rest is wasted). The hash table can store many more entries
+ * than slots using linear search. The table does not resize.
+ */
+#ifndef FLATCC_BUILDER_MIN_HASH_COUNT
+#define FLATCC_BUILDER_MIN_HASH_COUNT 64
+#endif
+
+typedef struct __flatcc_builder_buffer_frame __flatcc_builder_buffer_frame_t;
+struct __flatcc_builder_buffer_frame {
+ flatcc_builder_identifier_t identifier;
+ flatcc_builder_ref_t mark;
+ int flags;
+ size_t block_align;
+};
+
+typedef struct __flatcc_builder_vector_frame __flatcc_builder_vector_frame_t;
+struct __flatcc_builder_vector_frame {
+ flatbuffers_uoffset_t elem_size;
+ flatbuffers_uoffset_t count;
+ flatbuffers_uoffset_t max_count;
+};
+
+typedef struct __flatcc_builder_table_frame __flatcc_builder_table_frame_t;
+struct __flatcc_builder_table_frame {
+ flatbuffers_uoffset_t vs_end;
+ flatbuffers_uoffset_t pl_end;
+ uint32_t vt_hash;
+ flatbuffers_voffset_t id_end;
+};
+
+/*
+ * Store state for nested structures such as buffers, tables and vectors.
+ *
+ * For less busy data and data where access to a previous state is
+ * irrelevant, the frame may store the current state directly. Otherwise
+ * the current state is maintained in the flatcc_builder_t structure in a
+ * possibly derived form (e.g. ds pointer instead of ds_end offset) and
+ * the frame is used to store the previous state when the frame is
+ * entered.
+ *
+ * Most operations have a start/update/end cycle the decides the
+ * liftetime of a frame, but these generally also have a direct form
+ * (create) that does not use a frame at all. These still do some
+ * state updates notably passing min_align to parent which may also be
+ * an operation without a frame following the child level operation
+ * (e.g. create struct, create buffer). Ending a frame results in the
+ * same kind of updates.
+ */
+typedef struct __flatcc_builder_frame __flatcc_builder_frame_t;
+struct __flatcc_builder_frame {
+ flatbuffers_uoffset_t ds_first;
+ flatbuffers_uoffset_t type_limit;
+ flatbuffers_uoffset_t ds_offset;
+ uint16_t align;
+ uint16_t type;
+ union {
+ __flatcc_builder_table_frame_t table;
+ __flatcc_builder_vector_frame_t vector;
+ __flatcc_builder_buffer_frame_t buffer;
+ };
+};
+
+/**
+ * The main flatcc_builder structure. Can be stack allocated and must
+ * be initialized with `flatcc_builder_init` and cleared with
+ * `flatcc_builder_clear` to reclaim memory. Between buffer builds,
+ * `flatcc_builder_reset` may be used.
+ */
+typedef struct flatcc_builder flatcc_builder_t;
+
+struct flatcc_builder {
+ /* Next entry on reserved stack in `alloc_pl` buffer. */
+ flatbuffers_voffset_t *pl;
+ /* Next entry on reserved stack in `alloc_vs` buffer. */
+ flatbuffers_voffset_t *vs;
+ /* One above the highest entry in vs, used to track vt_size. */
+ flatbuffers_voffset_t id_end;
+ /* The evolving vtable hash updated with every new field. */
+ uint32_t vt_hash;
+
+ /* Pointer to ds_first. */
+ uint8_t *ds;
+ /* Offset from `ds` on current frame. */
+ flatbuffers_uoffset_t ds_offset;
+ /* ds buffer size relative to ds_first, clamped to max size of current type. */
+ flatbuffers_uoffset_t ds_limit;
+
+ /* ds_first, ds_first + ds_offset is current ds stack range. */
+ flatbuffers_uoffset_t ds_first;
+ /* Points to currently open frame in `alloc_fs` buffer. */
+ __flatcc_builder_frame_t *frame;
+
+ /* Only significant to emitter function, if at all. */
+ void *emit_context;
+ /* Only significant to allocator function, if at all. */
+ void *alloc_context;
+ /* Customizable write function that both appends and prepends data. */
+ flatcc_builder_emit_fun *emit;
+ /* Customizable allocator that also deallocates. */
+ flatcc_builder_alloc_fun *alloc;
+ /* Buffers indexed by `alloc_type` */
+ flatcc_iovec_t buffers[FLATCC_BUILDER_ALLOC_BUFFER_COUNT];
+ /* Number of slots in ht given as 1 << ht_width. */
+ size_t ht_width;
+
+ /* The location in vb to add next cached vtable. */
+ flatbuffers_uoffset_t vb_end;
+ /* Where to allocate next vtable descriptor for hash table. */
+ flatbuffers_uoffset_t vd_end;
+ /* Ensure final buffer is aligned to at least this. Nested buffers get their own `min_align`. */
+ uint16_t min_align;
+ /* The current active objects alignment isolated from nested activity. */
+ uint16_t align;
+ /* The current buffers block alignment used when emitting buffer. */
+ uint16_t block_align;
+ /* Signed virtual address range used for `flatcc_builder_ref_t` and emitter. */
+ flatcc_builder_ref_t emit_start;
+ flatcc_builder_ref_t emit_end;
+ /* 0 for top level, and end of buffer ref for nested buffers. */
+ flatcc_builder_ref_t buffer_mark;
+ /* Current nesting level. Helpful to state-machines with explicit stack and to check `max_level`. */
+ int level;
+ /* Aggregate check for allocated frame and max_level. */
+ int limit_level;
+ /* Track size prefixed buffer. */
+ int buffer_flags;
+
+ /* Settings that may happen with no frame allocated. */
+
+ flatcc_builder_identifier_t identifier;
+
+ /* Settings that survive reset (emitter, alloc, and contexts also survive): */
+
+ /* If non-zero, vtable cache gets flushed periodically. */
+ size_t vb_flush_limit;
+ /* If non-zero, fails on deep nesting to help drivers with a stack, such as recursive parsers etc. */
+ int max_level;
+ /* If non-zero, do not cluster vtables at end, only emit negative offsets (0 by default). */
+ int disable_vt_clustering;
+
+ /* Set if the default emitter is being used. */
+ int is_default_emitter;
+ /* Only used with default emitter. */
+ flatcc_emitter_t default_emit_context;
+
+ /* Offset to the last entered user frame on the user frame stack, after frame header, or 0. */
+ size_t user_frame_offset;
+
+ /* The offset to the end of the most recent user frame. */
+ size_t user_frame_end;
+};
+
+/**
+ * Call this before any other API call.
+ *
+ * The emitter handles the completed chunks of the buffer that will no
+ * longer be required by the builder. It is largely a `write` function
+ * that can append to both positive and negative offsets.
+ *
+ * No memory is allocated during init. Buffers will be allocated as
+ * needed. The `emit_context` is only used by the emitter, if at all.
+ *
+ * `flatcc_builder_reset/clear` calls are automtically forwarded to the
+ * default emitter.
+ *
+ * Returns -1 on failure, 0 on success.
+ */
+int flatcc_builder_init(flatcc_builder_t *B);
+
+/**
+ * Use instead of `flatcc_builder_init` when providing a custom allocator
+ * or emitter. Leave emitter or allocator null to use default.
+ * Cleanup of emit and alloc context must be handled manually after
+ * the builder is cleared or reset, except if emitter is null the
+ * default will be automatically cleared and reset.
+ *
+ * Returns -1 on failure, 0 on success.
+ */
+int flatcc_builder_custom_init(flatcc_builder_t *B,
+ flatcc_builder_emit_fun *emit, void *emit_context,
+ flatcc_builder_alloc_fun *alloc, void *alloc_context);
+
+/*
+ * Returns (flatcc_emitter_t *) if the default context is used.
+ * Other emitter might have null contexts.
+ */
+void *flatcc_builder_get_emit_context(flatcc_builder_t *B);
+
+/**
+ * Prepares builder for a new build. The emitter is not told when a
+ * buffer is finished or when a new begins, and must be told so
+ * separately. Allocated buffers will be zeroed, but may optionally be
+ * reduced to their defaults (signalled by reallocating each non-empty
+ * buffer to a single byte). General settings are cleared optionally,
+ * such as cache flushing. Buffer specific settings such as buffer
+ * identifier are always cleared.
+ *
+ * Returns -1 if allocator complains during buffer reduction, 0 on
+ * success.
+ */
+int flatcc_builder_custom_reset(flatcc_builder_t *B,
+ int reduce_buffers, int set_defaults);
+
+/*
+ * Same as `flatcc_builder_custom_reset` with default arguments
+ * where buffers are not reduced and default settings are not reset.
+ */
+int flatcc_builder_reset(flatcc_builder_t *B);
+
+/**
+ * Deallocates all memory by calling allocate with a zero size request
+ * on each buffer, then zeroing the builder structure itself.
+ */
+void flatcc_builder_clear(flatcc_builder_t *B);
+
+/**
+ * Allocates to next higher power of 2 using system realloc and ignores
+ * `alloc_context`. Only reduces size if a small subsequent increase in
+ * size would not trigger a reallocation. `alloc_type` is used to
+ * set minimum sizes. Hash tables are allocated to the exact requested
+ * size. See also `alloc_fun`.
+ */
+int flatcc_builder_default_alloc(void *alloc_context,
+ flatcc_iovec_t *b, size_t request, int zero_fill, int alloc_type);
+
+/**
+ * If non-zero, the vtable cache will get flushed whenever it reaches
+ * the given limit at a point in time where more space is needed. The
+ * limit is not exact as it is only tested when reallocation is
+ * required.
+ */
+void flatcc_builder_set_vtable_cache_limit(flatcc_builder_t *B, size_t size);
+
+/**
+ * Manual flushing of vtable for long running tasks. Mostly used
+ * internally to deal with nested buffers.
+ */
+void flatcc_builder_flush_vtable_cache(flatcc_builder_t *B);
+
+/**
+ * Low-level support function to aid in constructing nested buffers without
+ * allocation. Not for regular use.
+ *
+ * Call where `start_buffer` would have been placed when using
+ * `create_buffer` in a nested context. Save the return value on a stack
+ * as argument to `pop_buffer_alignment`.
+ *
+ * The call resets the current derived buffer alignment so the nested
+ * buffer will not be aligned to more than required.
+ *
+ * Often it will not be necessary to be so careful with alignment since
+ * the alignment cannot be invalid by failing to use push and pop, but
+ * for code generation it will ensure the correct result every time.
+ */
+uint16_t flatcc_builder_push_buffer_alignment(flatcc_builder_t *B);
+
+/**
+ * Low-level call.
+ *
+ * Call with the return value from push_buffer_alignment after a nested
+ * `create_buffer_call`. The alignments merge back up in the buffer
+ * hierarchy so the top level buffer gets the largest of all aligments.
+ */
+void flatcc_builder_pop_buffer_alignment(flatcc_builder_t *B, uint16_t buffer_align);
+
+/**
+ * This value may be of interest when the buffer has been ended, for
+ * example when subsequently allocating memory for the buffer to ensure
+ * that memory is properly aligned.
+ */
+uint16_t flatcc_builder_get_buffer_alignment(flatcc_builder_t *B);
+
+/**
+ * Level 0 means no buffer is started, otherwise it increments with
+ * start calls and decrements with end calls (approximately for
+ * optimized operations such as table vectors).
+ *
+ * If `max_level` has been set, `get_level` always returns a value <=
+ * `max_level` provided no start call has failed.
+ *
+ * Level continues to increment inside nested buffers.
+ */
+int flatcc_builder_get_level(flatcc_builder_t *B);
+
+/**
+ * Setting the max level triggers a failure on start of new nestings
+ * when the level is reached. May be used to protect recursive descend
+ * parsers etc. or later buffer readers.
+ *
+ * The builder itself is not sensitive to depth, and the allocator is a
+ * better way to protect resource abuse.
+ *
+ * `max_level` is not reset inside nested buffers.
+ */
+void flatcc_builder_set_max_level(flatcc_builder_t *B, int level);
+
+/**
+ * By default ordinary data such as tables are placed in front of
+ * earlier produced content and vtables are placed at the very end thus
+ * clustering vtables together. This can be disabled so all content is
+ * placed in front. Nested buffers ignores this setting because they can
+ * only place content in front because they cannot blend with the
+ * containing buffers content. Clustering could be more cache friendly
+ * and also enables pre-shipping of the vtables during transmission.
+ */
+void flatcc_builder_set_vtable_clustering(flatcc_builder_t *B, int enable);
+
+enum flatcc_builder_buffer_flags {
+ flatcc_builder_is_nested = 1,
+ flatcc_builder_with_size = 2,
+};
+
+/**
+ * An alternative to start buffer, start struct/table ... end buffer.
+ *
+ * This call is mostly of interest as a means to quicly create a zero
+ * allocation top-level buffer header following a call to create_struct,
+ * or to create_vtable/create_table. For that, it is quite simple to
+ * use. For general buffer construction without allocation, more care is
+ * needed, as discussed below.
+ *
+ * If the content is created with `start/end_table` calls, or similar,
+ * it is better to use `start/end_buffer` since stack allocation is used
+ * anyway.
+ *
+ * The buffer alignment must be provided manually as it is not derived
+ * from constructed content, unlike `start/end_buffer`. Typically
+ * `align` would be same argument as provided to `create_struct`.
+ * `get_buffer_alignment` may also used (note: `get_buffer_alignment`
+ * may return different after the call because it will be updated with
+ * the `block_align` argument to `create_buffer` but that is ok).
+ *
+ * The buffer may be constructed as a nested buffer with the `is_nested
+ * = 1` flag. As a nested buffer a ubyte vector header is placed before
+ * the aligned buffer header. A top-level buffer will normally have
+ * flags set to 0.
+ *
+ * A top-level buffer may also be constructed with the `with_size = 2`
+ * flag for top level buffers. It adds a size prefix similar to
+ * `is_nested` but the size is part of the aligned buffer. A size
+ * prefixed top level buffer must be accessed with a size prefix aware
+ * reader, or the buffer given to a standard reader must point to after
+ * the size field while keeping the buffer aligned to the size field
+ * (this will depend on the readers API which may be an arbitrary other
+ * language).
+ *
+ * If the `with_size` is used with the `is_nested` flag, the size is
+ * added as usual and all fields remain aligned as before, but padding
+ * is adjusted to ensure the buffer is aligned to the size field so
+ * that, for example, the nested buffer with size can safely be copied
+ * to a new memory buffer for consumption.
+ *
+ * Generally, references may only be used within the same buffer
+ * context. With `create_buffer` this becomes less precise. The rule
+ * here is that anything that would be valid with start/end_buffer
+ * nestings is also valid when removing the `start_buffer` call and
+ * replacing `end_buffer` with `create_buffer`.
+ *
+ * Note the additional burden of tracking buffer alignment manually -
+ * To help with this use `push_buffer_alignment` where `start_buffer`
+ * would have been placed, and `pop_buffer_alignment after the
+ * `create_buffer` call, and use `get_buffer_alignemnt` as described
+ * above.
+ *
+ * `create_buffer` is not suitable as a container for buffers created
+ * with `start/end_buffer` as these make assumptions about context that
+ * create buffer does not provide. Also, there is no point in doing so,
+ * since the idea of `create_buffer` is to avoid allocation in the first
+ * place.
+ */
+flatcc_builder_ref_t flatcc_builder_create_buffer(flatcc_builder_t *B,
+ const char identifier[FLATBUFFERS_IDENTIFIER_SIZE],
+ uint16_t block_align,
+ flatcc_builder_ref_t ref, uint16_t align, int flags);
+
+/**
+ * Creates a struct within the current buffer without using any
+ * allocation. Formally the struct should be used as a root in the
+ * `end_buffer` call as there are no other way to use struct while
+ * conforming to the FlatBuffer format - noting that tables embed
+ * structs in their own data area.
+ *
+ * The struct should be in little endian format and follow the usual
+ * FlatBuffers alignment rules, although this API won't care about what
+ * is being stored.
+ *
+ * May also be used to simply emit a struct through the emitter
+ * interface without being in a buffer and without being a valid
+ * FlatBuffer.
+ */
+flatcc_builder_ref_t flatcc_builder_create_struct(flatcc_builder_t *B,
+ const void *data, size_t size, uint16_t align);
+
+/**
+ * Starts a struct and returns a pointer that should be used immediately
+ * to fill in the struct in protocol endian format, and when done,
+ * `end_struct` should be called. The returned reference should be used
+ * as argument to `end_buffer`. See also `create_struct`.
+ */
+void *flatcc_builder_start_struct(flatcc_builder_t *B,
+ size_t size, uint16_t align);
+
+/**
+ * Return a pointer also returned at start struct, e.g. for endian
+ * conversion.
+ */
+void *flatcc_builder_struct_edit(flatcc_builder_t *B);
+
+/**
+ * Emits the struct started by `start_struct` and returns a reference to
+ * be used as root in an enclosing `end_buffer` call.
+ * As mentioned in `create_struct`, these can also be used more freely,
+ * but not while being conformant FlatBuffers.
+ */
+flatcc_builder_ref_t flatcc_builder_end_struct(flatcc_builder_t *B);
+
+/**
+ * The buffer always aligns to at least the offset size (typically 4)
+ * and the internal alignment requirements of the buffer content which
+ * is derived as content is added.
+ *
+ * In addition, block_align can be specified. This ensures the resulting
+ * buffer is at least aligned to the block size and that the total size
+ * is zero padded to fill a block multiple if necessary. Because the
+ * emitter operates on a virtual address range before the full buffer is
+ * aligned, it may have to make assumptions based on that: For example,
+ * it may be processing encryption blocks in the fly, and the resulting
+ * buffer should be aligned to the encryption block size, even if the
+ * content is just a byte aligned struct. Block align helps ensure this.
+ * If the block align as 1 there will be no attempt to zero pad at the
+ * end, but the content may still warrant padding after the header. End
+ * padding is only needed with clustered vtables (which is the default).
+ *
+ * `block_align` is allowed to be 0 meaning it will inherit from parent if
+ * present, and otherwise it defaults to 1.
+ *
+ * The identifier may be null, and it may optionally be set later with
+ * `set_identifier` before the `end_buffer` call.
+ *
+ * General note:
+ *
+ * Only references returned with this buffer as current (i.e. last
+ * unended buffer) can be stored in other objects (tables, offset
+ * vectors) also belonging to this buffer, or used as the root argument
+ * to `end_buffer`. A reference may be stored at most once, and unused
+ * references will result in buffer garbage. All calls must be balanced
+ * around the respective start / end operations, but may otherwise nest
+ * freely, including nested buffers. Nested buffers are supposed to be
+ * stored in a table offset field to comply with FlatBuffers, but the
+ * API does not place any restrictions on where references are stored,
+ * as long as they are indicated as offset fields.
+ *
+ * All alignment in all API calls must be between 1 and 256 and must be a
+ * power of 2. This is not checked. Only if explicitly documented can it
+ * also be 0 for a default value.
+ *
+ * `flags` can be `with_size` but `is_nested` is derived from context
+ * see also `create_buffer`.
+ */
+int flatcc_builder_start_buffer(flatcc_builder_t *B,
+ const char identifier[FLATBUFFERS_IDENTIFIER_SIZE],
+ uint16_t block_align, int flags);
+
+/**
+ * The root object should be a struct or a table to conform to the
+ * FlatBuffers format, but technically it can also be a vector or a
+ * string, or even a child buffer (which is also vector as seen by the
+ * buffer). The object must be created within the current buffer
+ * context, that is, while the current buffer is the deepest nested
+ * buffer on the stack.
+ */
+flatcc_builder_ref_t flatcc_builder_end_buffer(flatcc_builder_t *B, flatcc_builder_ref_t root);
+
+/**
+ * The embed buffer is mostly intended to add an existing buffer as a
+ * nested buffer. The buffer will be wrapped in a ubyte vector such that
+ * the buffer is aligned at vector start, after the size field.
+ *
+ * If `align` is 0 it will default to 8 so that all FlatBuffer numeric
+ * types will be readable. NOTE: generally do not count on align 0 being
+ * valid or even checked by the API, but in this case it may be
+ * difficult to know the internal buffer alignment, and 1 would be the wrong
+ * choice.
+ *
+ * If `block_align` is set (non-zero), the buffer is placed in an isolated
+ * block multiple. This may cost up to almost 2 block sizes in padding.
+ * If the `block_align` argument is 0, it inherits from the parent
+ * buffer block_size, or defaults to 1.
+ *
+ * The `align` argument must be set to respect the buffers internal
+ * alignment requirements, but if the buffer is smaller it will not be
+ * padded to isolate the buffer. For example a buffer of with
+ * `align = 64` and `size = 65` may share its last 64 byte block with
+ * other content, but not if `block_align = 64`.
+ *
+ * Because the ubyte size field is not, by default, part of the aligned
+ * buffer, significant space can be wasted if multiple blocks are added
+ * in sequence with a large block size.
+ *
+ * In most cases the distinction between the two alignments is not
+ * important, but it allows separate configuration of block internal
+ * alignment and block size, which can be important for auto-generated
+ * code that may know the alignment of the buffer, but not the users
+ * operational requirements.
+ *
+ * If the buffer is embedded without a parent buffer, it will simply
+ * emit the buffer through the emit interface, but may also add padding
+ * up to block alignment. At top-level there will be no size field
+ * header.
+ *
+ * If `with_size` flag is set, the buffer is aligned to size field and
+ * the above note about padding space no longer applies. The size field
+ * is added regardless. The `is_nested` flag has no effect since it is
+ * impplied.
+ */
+flatcc_builder_ref_t flatcc_builder_embed_buffer(flatcc_builder_t *B,
+ uint16_t block_align,
+ const void *data, size_t size, uint16_t align, int flags);
+
+/**
+ * Applies to the innermost open buffer. The identifier may be null or
+ * contain all zero. Overrides any identifier given to the start buffer
+ * call.
+ */
+void flatcc_builder_set_identifier(flatcc_builder_t *B,
+ const char identifier[FLATBUFFERS_IDENTIFIER_SIZE]);
+
+enum flatcc_builder_type {
+ flatcc_builder_empty = 0,
+ flatcc_builder_buffer,
+ flatcc_builder_struct,
+ flatcc_builder_table,
+ flatcc_builder_vector,
+ flatcc_builder_offset_vector,
+ flatcc_builder_string
+};
+
+/**
+ * Returns the object type currently on the stack, for example if
+ * needing to decide how to close a buffer. Because a table is
+ * automatically added when starting a table buffer,
+ * `flatcc_builder_table_buffer` should not normally be seen and the level
+ * should be 2 before when closing a top-level table buffer, and 0
+ * after. A `flatcc_builder_struct_buffer` will be visible at level 1.
+ *
+ */
+enum flatcc_builder_type flatcc_builder_get_type(flatcc_builder_t *B);
+
+/**
+ * Similar to `get_type` but for a specific level. `get_type_at(B, 1)`
+ * will return `flatcc_builder_table_buffer` if this is the root buffer
+ * type. get_type_at(B, 0) is always `flatcc_builder_empty` and so are any
+ * level above `get_level`.
+ */
+enum flatcc_builder_type flatcc_builder_get_type_at(flatcc_builder_t *B, int level);
+
+/**
+ * The user stack is available for custom data. It may be used as
+ * a simple stack by extending or reducing the inner-most frame.
+ *
+ * A frame has a size and a location on the user stack. Entering
+ * a frame ensures the start is aligned to sizeof(size_t) and
+ * ensures the requested space is available without reallocation.
+ * When exiting a frame, the previous frame is restored.
+ *
+ * A user frame works completely independently of the builders
+ * frame stack for tracking tables vectors etc. and does not have
+ * to be completely at exit, but obviously it is not valid to
+ * exit more often the entered.
+ *
+ * The frame is zeroed when entered.
+ *
+ * The returned pointer is only valid until the next call to
+ * `enter/extend_user_frame`. The latest frame pointer
+ * can be restored by calling `get_user_frame`.
+ */
+void *flatcc_builder_enter_user_frame(flatcc_builder_t *B, size_t size);
+
+/**
+ * Makes the parent user frame current, if any. It is not valid to call
+ * if there isn't any frame.
+ */
+void flatcc_builder_exit_user_frame(flatcc_builder_t *B);
+
+/**
+ * Returns a pointer to the start of the inner-most user frame. It is
+ * not valid to call if there isn't any fram.
+ */
+void *flatcc_builder_get_user_frame(flatcc_builder_t *B);
+
+/**
+ * Returns 1 if there is a user frame, and 0 otherwise.
+ */
+int flatcc_builder_has_user_frame(flatcc_builder_t *B);
+
+
+/**
+ * Returns the size of the buffer and the logical start and end address
+ * of with respect to the emitters address range. `end` - `start` also
+ * yields the size. During construction `size` is the emitted number of
+ * bytes and after buffer close it is the actual buffer size - by then
+ * the start is also the return value of close buffer. End marks the end
+ * of the virtual table cluster block.
+ *
+ * NOTE: there is no guarantee that all vtables end up in the cluster
+ * block if there is placed a limit on the vtable size, or if nested
+ * buffers are being used. On the other hand, if these conditions are
+ * met, it is guaranteed that all vtables are present if the vtable
+ * block is available (this depends on external transmission - the
+ * vtables are always emitted before tables using them). In all cases
+ * the vtables will behave as valid vtables in a flatbuffer.
+ */
+size_t flatcc_builder_get_buffer_size(flatcc_builder_t *B);
+
+/**
+ * Returns the reference to the start of the emitter buffer so far, or
+ * in total after buffer end, in the virtual address range used
+ * by the emitter. Start is also returned by buffer end.
+ */
+flatcc_builder_ref_t flatcc_builder_get_buffer_start(flatcc_builder_t *B);
+
+/**
+ * Returns the reference to the end of buffer emitted so far. When
+ * clustering vtables, this is the end of tables, or after buffer end,
+ * also zero padding if block aligned. If clustering is disabled, this
+ * method will return 0 as the buffer only grows down then.
+ */
+flatcc_builder_ref_t flatcc_builder_get_buffer_mark(flatcc_builder_t *B);
+
+/**
+ * Creates the vtable in the current buffer context, somewhat similar to
+ * how create_vector operates. Each call results in a new table even if
+ * an identical has already been emitted.
+ *
+ * Also consider `create_cached_vtable` which will reuse existing
+ * vtables.
+ *
+ * This is low-low-level function intended to support
+ * `create_cached_vtable` or equivalent, and `create_table`, both of
+ * which are normally used indirectly via `start_table`, `table_add`,
+ * `table_add_offset`..., `table_end`.
+ *
+ * Creates a vtable as a verbatim copy. This means the vtable must
+ * include the header fields containing the vtable size and the table
+ * size in little endian voffset_t encoding followed by the vtable
+ * entries in same encoding.
+ *
+ * The function may be used to copy vtables from other other buffers
+ * since they are directly transferable.
+ *
+ * The returned reference is actually the emitted location + 1. This
+ * ensures the vtable is not mistaken for error because 0 is a valid
+ * vtable reference. `create_table` is aware of this and substracts one
+ * before computing the final offset relative to the table. This also
+ * means vtable references are uniquely identifiable by having the
+ * lowest bit set.
+ *
+ * vtable references may be reused within the same buffer, not any
+ * parent or other related buffer (technically this is possible though,
+ * as long as it is within same builder context, but it will not construct
+ * valid FlatBuffers because the buffer cannot be extracted in isolation).
+ */
+flatcc_builder_vt_ref_t flatcc_builder_create_vtable(flatcc_builder_t *B,
+ const flatbuffers_voffset_t *vt,
+ flatbuffers_voffset_t vt_size);
+
+/**
+ * Support function to `create_vtable`. See also the uncached version
+ * `create_vtable`.
+ *
+ * Looks up the constructed vtable on the vs stack too see if it matches
+ * a cached entry. If not, it emits a new vtable either at the end if
+ * top-level and clustering is enabled, or at the front (always for
+ * nested buffers).
+ *
+ * If the same vtable was already emitted in a different buffer, but not
+ * in the current buffer, the cache entry will be reused, but a new
+ * table will be emitted the first it happens in the same table.
+ *
+ * The returned reference is + 1 relative to the emitted address range
+ * to identify it as a vtable and to avoid mistaking the valid 0
+ * reference for an error (clustered vtables tend to start at the end at
+ * the virtual address 0, and up).
+ *
+ * The hash function can be chosen arbitrarily but may result in
+ * duplicate emitted vtables if different hash functions are being used
+ * concurrently, such as mixing the default used by `start/end table`
+ * with a custom function (this is not incorrect, it only increases the
+ * buffer size and cache pressure).
+ *
+ * If a vtable has a unique ID by other means than hashing the content,
+ * such as an integer id, and offset into another buffer, or a pointer,
+ * a good hash may be multiplication by a 32-bit prime number. The hash
+ * table is not very sensitive to collissions as it uses externally
+ * chained hashing with move to front semantics.
+ */
+flatcc_builder_vt_ref_t flatcc_builder_create_cached_vtable(flatcc_builder_t *B,
+ const flatbuffers_voffset_t *vt,
+ flatbuffers_voffset_t vt_size, uint32_t vt_hash);
+
+/*
+ * Based on Knuth's prime multiplier.
+ *
+ * This is an incremental hash that is called with id and size of each
+ * non-empty field, and finally with the two vtable header fields
+ * when vtables are constructed via `table_add/table_add_offset`.
+ *
+ */
+#ifndef FLATCC_SLOW_MUL
+#ifndef FLATCC_BUILDER_INIT_VT_HASH
+#define FLATCC_BUILDER_INIT_VT_HASH(hash) { (hash) = (uint32_t)0x2f693b52UL; }
+#endif
+#ifndef FLATCC_BUILDER_UPDATE_VT_HASH
+#define FLATCC_BUILDER_UPDATE_VT_HASH(hash, id, offset) \
+ { (hash) = (((((uint32_t)id ^ (hash)) * (uint32_t)2654435761UL)\
+ ^ (uint32_t)(offset)) * (uint32_t)2654435761UL); }
+#endif
+#ifndef FLATCC_BUILDER_BUCKET_VT_HASH
+#define FLATCC_BUILDER_BUCKET_VT_HASH(hash, width) (((uint32_t)(hash)) >> (32 - (width)))
+#endif
+#endif
+
+/*
+ * By default we use Bernsteins hash as fallback if multiplication is slow.
+ *
+ * This just have to be simple, fast, and work on devices without fast
+ * multiplication. We are not too sensitive to collisions. Feel free to
+ * experiment and replace.
+ */
+#ifndef FLATCC_BUILDER_INIT_VT_HASH
+#define FLATCC_BUILDER_INIT_VT_HASH(hash) { (hash) = 5381; }
+#endif
+#ifndef FLATCC_BUILDER_UPDATE_VT_HASH
+#define FLATCC_BUILDER_UPDATE_VT_HASH(hash, id, offset) \
+ { (hash) = ((((hash) << 5) ^ (id)) << 5) ^ (offset); }
+#endif
+#ifndef FLATCC_BUILDER_BUCKET_VT_HASH
+#define FLATCC_BUILDER_BUCKET_VT_HASH(hash, width) (((1 << (width)) - 1) & (hash))
+#endif
+
+
+
+/**
+ * Normally use `start_table` instead of this call.
+ *
+ * This is a low-level call only intended for high-performance
+ * applications that repeatedly churn about similar tables of known
+ * layout, or as a support layer for other builders that maintain their
+ * own allocation rather than using the stack of this builder.
+ *
+ * Creates a table from an already emitted vtable, actual data that is
+ * properly aligned relative to data start and in little endian
+ * encoding. Unlike structs, tables can have offset fields. These must
+ * be stored as flatcc_builder_ref_t types (which have uoffset_t size) as
+ * returned by the api in native encoding. The `offsets` table contain
+ * voffsets relative to `data` start (this is different from how vtables
+ * store offsets because they are relative to a table header). The
+ * `offsets` table is only used temporarily to translate the stored
+ * references and is not part of final buffer content. `offsets` may be
+ * null if `offset_count` is 0. `align` should be the highest aligned
+ * field in the table, but `size` need not be a multiple of `align`.
+ * Aside from endian encoding, the vtable must record a table size equal
+ * to `size + sizeof(flatbuffers_uoffset_t)` because it includes the
+ * table header field size. The vtable is not accessed by this call (nor
+ * is it available). Unlike other references, the vtable reference may
+ * be shared between tables in the same buffer (not with any related
+ * buffer such as a parent buffer).
+ *
+ * The operation will not use any allocation, but will update the
+ * alignment of the containing buffer if any.
+ *
+ * Note: unlike other create calls, except `create_offset_vector`,
+ * the source data is modified in order to translate references intok
+ * offsets before emitting the table.
+ */
+flatcc_builder_ref_t flatcc_builder_create_table(flatcc_builder_t *B,
+ const void *data, size_t size, uint16_t align,
+ flatbuffers_voffset_t *offsets, int offset_count,
+ flatcc_builder_vt_ref_t vt_ref);
+
+/**
+ * Starts a table, typically following a start_buffer call as an
+ * alternative to starting a struct, or to create table fields to be
+ * stored in a parent table, or in an offset vector.
+ * A number of `table_add` and table_add_offset` call may be placed
+ * before the `end_table` call. Struct fields should NOT use `struct`
+ * related call (because table structs are in-place), rather they should
+ * use the `table_add` call with the appropriate size and alignment.
+ *
+ * A table, like other reference returning calls, may also be started
+ * outside a buffer if the buffer header and alignment is of no
+ * interest to the application, for example as part of an externally
+ * built buffer.
+ *
+ * `count` must be larger than the largest id used for this table
+ * instance. Normally it is set to the number of fields defined in the
+ * schema, but it may be less if memory is constrained and only few
+ * fields with low valued id's are in use. The count can extended later
+ * with `reserve_table` if necessary. `count` may be also be set to a
+ * large enough value such as FLATBUFFERS_ID_MAX + 1 if memory is not a
+ * concern (reserves about twice the maximum vtable size to track the
+ * current vtable and voffsets where references must be translated to
+ * offsets at table end). `count` may be zero if for example
+ * `reserve_table` is being used.
+ *
+ * Returns -1 on error, 0 on success.
+ */
+int flatcc_builder_start_table(flatcc_builder_t *B, int count);
+
+/**
+ * Call before adding a field with an id that is not below the count set
+ * at table start. Not needed in most cases. For performance reasons
+ * the builder does not check all bounds all the the time, but the user
+ * can do so if memory constraints prevent start_table from using a
+ * conservative value. See also `table_start`.
+ *
+ * Note: this call has absolutely no effect on the table layout, it just
+ * prevents internal buffer overruns.
+ *
+ * Returns -1 on error, 0 on success.
+ */
+int flatcc_builder_reserve_table(flatcc_builder_t *B, int count);
+
+/**
+ * Completes the table constructed on the internal stack including
+ * emitting a vtable, or finding a matching vtable that has already been
+ * emitted to the same buffer. (Vtables cannot be shared between
+ * buffers, but they can between tables of the same buffer).
+ *
+ * Note: there is a considerable, but necessary, amount of bookkeeping
+ * involved in constructing tables. The `create_table` call is much
+ * faster, but it also expects a lot of work to be done already.
+ *
+ * Tables can be created with no fields added. This will result in an
+ * empty vtable and a table with just a vtable reference. If a table is
+ * used as a sub-table, such a table might also not be stored at all,
+ * but we do not return a special reference for that, nor do we provide
+ * and option to not create the table in this case. This may be
+ * interpreted as the difference between a null table (not stored in
+ * parent), and an empty table with a unique offset (and thus identity)
+ * different from other empty tables.
+ */
+flatcc_builder_ref_t flatcc_builder_end_table(flatcc_builder_t *B);
+
+/**
+ * Optionally this method can be called just before `flatcc_builder_end_table`
+ * to verify that all required fields have been set.
+ * Each entry is a table field id.
+ *
+ * Union fields should use the type field when checking for presence and
+ * may also want to check the soundness of the union field overall using
+ * `check_union_field` with the id one higher than the type field id.
+ *
+ * This funcion is typically called by an assertion in generated builder
+ * interfaces while release builds may want to avoid this performance
+ * overhead.
+ *
+ * Returns 1 if all fields are matched, 0 otherwise.
+ */
+int flatcc_builder_check_required(flatcc_builder_t *B, const flatbuffers_voffset_t *required, int count);
+
+/**
+ * Same as `check_required` when called with a single element.
+ *
+ * Typically used when direct calls are more convenient than building an
+ * array first. Useful when dealing with untrusted intput such as parsed
+ * text from an external source.
+ */
+int flatcc_builder_check_required_field(flatcc_builder_t *B, flatbuffers_voffset_t id);
+
+/**
+ * Checks that a union field is valid.
+ *
+ * The criteria is:
+ *
+ * If the type field is not present (at id - 1), or it holds a zero value,
+ * then the table field (at id) must be present.
+ *
+ * Generated builder code may be able to enforce valid unions without
+ * this check by setting both type and table together, but e.g. parsers
+ * may receive the type and the table independently and then it makes
+ * sense to validate the union fields before table completion.
+ *
+ * Note that an absent union field is perfectly valid. If a union is
+ * required, the type field (id - 1), should be checked separately
+ * while the table field should only be checked here because it can
+ * (and must) be absent when the type is NONE (= 0).
+ */
+int flatcc_builder_check_union_field(flatcc_builder_t *B, flatbuffers_voffset_t id);
+
+/**
+ * A struct, enum or scalar added should be stored in little endian in
+ * the return pointer location. The pointer is short lived and will
+ * not necessarily survive other builder calls.
+ *
+ * A union type field can also be set using this call. In fact, this is
+ * the only way to deal with unions via this API. Consequently, it is
+ * the users repsonsibility to ensure the appropriate type is added
+ * at the next higher id.
+ *
+ * Null and default values:
+ *
+ * FlatBuffers does not officially provide an option for null values
+ * because it does not distinguish between default values and values
+ * that are not present. At this api level, we do not deal with defaults
+ * at all. Callee should test the stored value against the default value
+ * and only add the field if it does not match the default. This only
+ * applies to scalar and enum values. Structs cannot have defaults so
+ * their absence means null, and strings, vectors and subtables do have
+ * natural null values different from the empty object and empty objects
+ * with different identity is also possible.
+ *
+ * To handle Null for scalars, the following approach is recommended:
+ *
+ * Provide a schema-specific `add` operation that only calls this
+ * low-level add method if the default does not match, and also provide
+ * another `set` operation that always stores the value, regardless of
+ * default. For most readers this will be transparent, except for extra
+ * space used, but for Null aware readers, these can support operations
+ * to test for Null/default/other value while still supporting the
+ * normal read operation that returns default when a value is absent
+ * (i.e. Null).
+ *
+ * It is valid to call with a size of 0 - the effect being adding the
+ * vtable entry. The call may also be dropped in this case to reduce
+ * the vtable size - the difference will be in null detection.
+ */
+void *flatcc_builder_table_add(flatcc_builder_t *B, int id, size_t size, uint16_t align);
+
+/**
+ * Returns a pointer to the buffer holding the last field added. The
+ * size argument must match the field size added. May, for example, be
+ * used to perform endian conversion after initially updating field
+ * as a native struct. Must be called before the table is ended.
+ */
+void *flatcc_builder_table_edit(flatcc_builder_t *B, size_t size);
+
+/**
+ * Similar to `table_add` but copies source data into the buffer before
+ * it is returned. Useful when adding a larger struct already encoded in
+ * little endian.
+ */
+void *flatcc_builder_table_add_copy(flatcc_builder_t *B, int id, const void *data, size_t size, uint16_t align);
+
+/**
+ * Add a string, vector, or sub-table depending on the type if the
+ * field identifier. The offset ref obtained when the field object was
+ * closed should be stored as is in the given pointer. The pointer
+ * is only valid short term, so create the object before calling
+ * add to table, but the owner table can be started earlier. Never mix
+ * refs from nested buffers with parent buffers.
+ *
+ * Also uses this method to add nested buffers. A nested buffer is
+ * simple a buffer created while another buffer is open. The buffer
+ * close operation provides the necessary reference.
+ *
+ * When the table closes, all references get converted into offsets.
+ * Before that point, it is not required that the offset is written
+ * to.
+ */
+flatcc_builder_ref_t *flatcc_builder_table_add_offset(flatcc_builder_t *B, int id);
+
+/**
+ * Creates a vector in a single operation using an externally supplied
+ * buffer. This completely bypasses the stack, but the size must be
+ * known and the content must be little endian. Do not use for strings
+ * and offset vectors. Other flatbuffer vectors could be used as a
+ * source, but the length prefix is not required.
+ *
+ * Set `max_count` to `FLATBUFFERS_COUNT_MAX(elem_size)` before a call
+ * to any string or vector operation to the get maximum safe vector
+ * size, or use (size_t)-1 if overflow is not a concern.
+ *
+ * The max count property is a global property that remains until
+ * explicitly changed.
+ *
+ * `max_count` is to prevent malicous or accidental overflow which is
+ * difficult to detect by multiplication alone, depending on the type
+ * sizes being used and having `max_count` thus avoids a division for
+ * every vector created. `max_count` does not guarantee a vector will
+ * fit in an empty buffer, it just ensures the internal size checks do
+ * not overflow. A safe, sane limit woud be max_count / 4 because that
+ * is half the maximum buffer size that can realistically be
+ * constructed, corresponding to a vector size of `UOFFSET_MAX / 4`
+ * which can always hold the vector in 1GB excluding the size field when
+ * sizeof(uoffset_t) = 4.
+ */
+flatcc_builder_ref_t flatcc_builder_create_vector(flatcc_builder_t *B,
+ const void *data, size_t count, size_t elem_size, uint16_t align, size_t max_count);
+
+/**
+ * Starts a vector on the stack.
+ *
+ * Do not use these calls for string or offset vectors, but do store
+ * scalars, enums and structs, always in little endian encoding.
+ *
+ * Use `extend_vector` subseequentlu to add zero, one or more elements
+ * at time.
+ *
+ * See `create_vector` for `max_count` argument (strings and offset
+ * vectors have a fixed element size and does not need this argument).
+ *
+ * Returns 0 on success.
+ */
+int flatcc_builder_start_vector(flatcc_builder_t *B, size_t elem_size,
+ uint16_t align, size_t max_count);
+
+/**
+ * Emits the vector constructed on the stack by start_vector.
+ *
+ * The vector may be accessed in the emitted stream using the returned
+ * reference, even if the containing buffer is still under construction.
+ * This may be useful for sorting. This api does not support sorting
+ * because offset vectors cannot read their references after emission,
+ * and while plain vectors could be sorted, it has been chosen that this
+ * task is better left as a separate processing step. Generated code can
+ * provide sorting functions that work on final in-memory buffers.
+ */
+flatcc_builder_ref_t flatcc_builder_end_vector(flatcc_builder_t *B);
+
+/** Returns the number of elements currently on the stack. */
+size_t flatcc_builder_vector_count(flatcc_builder_t *B);
+
+/**
+ * Returns a pointer ot the first vector element on stack,
+ * accessible up to the number of elements currently on stack.
+ */
+void *flatcc_builder_vector_edit(flatcc_builder_t *B);
+
+/**
+ * Similar to `end_vector` but updates all stored references so they
+ * become offsets to the vector start.
+ */
+flatcc_builder_ref_t flatcc_builder_end_offset_vector(flatcc_builder_t *B);
+
+/** Returns the number of elements currently on the stack. */
+size_t flatcc_builder_offset_vector_count(flatcc_builder_t *B);
+
+/**
+ * Returns a pointer ot the first vector element on stack,
+ * accessible up to the number of elements currently on stack.
+ */
+void *flatcc_builder_offset_vector_edit(flatcc_builder_t *B);
+
+
+/**
+ * Returns a zero initialized buffer to a new region of the vector which
+ * is extended at the end. The buffer must be consumed before other api
+ * calls that may affect the stack, including `extend_vector`.
+ *
+ * Do not use for strings or offset vectors. May be used for nested
+ * buffers, but these have dedicated calls to provide better alignment.
+ */
+void *flatcc_builder_extend_vector(flatcc_builder_t *B, size_t count);
+
+/**
+ * A specialized `vector_extend` that pushes a single element.
+ *
+ * Returns the buffer holding a modifiable copy of the added content,
+ * or null on error. Note: for structs, care must be taken to ensure
+ * the source has been zero padded. For this reason it may be better to
+ * use extend(B, 1) and assign specific fields instead.
+ */
+void *flatcc_builder_vector_push(flatcc_builder_t *B, const void *data);
+
+/**
+ * Pushes multiple elements at a time.
+ *
+ * Returns the buffer holding a modifiable copy of the added content,
+ * or null on error.
+ */
+void *flatcc_builder_append_vector(flatcc_builder_t *B, const void *data, size_t count);
+
+/**
+ * Removes elements already added to vector that has not been ended.
+ * For example, a vector of parsed list may remove the trailing comma,
+ * or the vector may simply overallocate to get some temporary working
+ * space. The total vector size must never become negative.
+ *
+ * Returns -1 if the count as larger than current count, or 0 on success.
+ */
+int flatcc_builder_truncate_vector(flatcc_builder_t *B, size_t count);
+
+/*
+ * Similar to `create_vector` but with references that get translated
+ * into offsets. The references must, as usual, belong to the current
+ * buffer. Strings, scalar and struct vectors can emit directly without
+ * stack allocation, but offset vectors must translate the offsets
+ * and therefore need the temporary space. Thus, this function is
+ * roughly equivalent to to start, append, end offset vector.
+ *
+ * See also `flatcc_builder_create_offset_vector_direct`.
+ */
+flatcc_builder_ref_t flatcc_builder_create_offset_vector(flatcc_builder_t *B,
+ const flatcc_builder_ref_t *data, size_t count);
+
+/*
+ * NOTE: this call takes non-const source vector of references
+ * and destroys the content.
+ *
+ * This is a faster version of `create_offset_vector` where the
+ * source references are destroyed. In return the vector can be
+ * emitted directly without passing over the stack.
+ */
+flatcc_builder_ref_t flatcc_builder_create_offset_vector_direct(flatcc_builder_t *B,
+ flatcc_builder_ref_t *data, size_t count);
+
+
+/**
+ * Starts a vector holding offsets to tables or strings. Before
+ * completion it will hold `flatcc_builder_ref_t` references because the
+ * offset is not known until the vector start location is known, which
+ * depends to the final size, which for parsers is generally unknown.
+ */
+int flatcc_builder_start_offset_vector(flatcc_builder_t *B);
+
+/**
+ * Similar to `extend_vector` but returns a buffer indexable as
+ * `flatcc_builder_ref_t` array. All elements must be set to a valid
+ * unique non-null reference, but truncate and extend may be used to
+ * perform edits. All produced references must be stored at most
+ * once in the buffer, including vectors. There are no checks, and this
+ * is an easy place to make mistakes. Unused references will leave
+ * garbage in the buffer. References should not originate from any other
+ * buffer than the current, including parents and nested buffers.
+ */
+flatcc_builder_ref_t *flatcc_builder_extend_offset_vector(flatcc_builder_t *B, size_t count);
+
+/** Similar to truncate_vector. */
+int flatcc_builder_truncate_offset_vector(flatcc_builder_t *B, size_t count);
+
+/**
+ * A specialized extend that pushes a single element.
+ *
+ * Returns the buffer holding a modifiable copy of the added content,
+ * or null on error.
+ */
+flatcc_builder_ref_t *flatcc_builder_offset_vector_push(flatcc_builder_t *B,
+ flatcc_builder_ref_t ref);
+
+/**
+ * Takes an array of refs as argument to do a multi push operation.
+ *
+ * Returns the buffer holding a modifiable copy of the added content,
+ * or null on error.
+ */
+flatcc_builder_ref_t *flatcc_builder_append_offset_vector(flatcc_builder_t *B,
+ const flatcc_builder_ref_t *refs, size_t count);
+
+/**
+ * Faster string operation that avoids temporary stack storage. The
+ * string is not required to be zero-terminated, but is expected
+ * (unchecked) to be utf-8. Embedded zeroes would be allowed but
+ * ubyte vectors should be used for that. The resulting string will
+ * have a zero termination added, not included in length.
+ */
+flatcc_builder_ref_t flatcc_builder_create_string(flatcc_builder_t *B,
+ const char *s, size_t len);
+
+/** `create_string` up to zero termination of source. */
+flatcc_builder_ref_t flatcc_builder_create_string_str(flatcc_builder_t *B,
+ const char *s);
+
+/**
+ * `create_string` up to zero termination or at most max_len of source.
+ *
+ * Note that like `strncpy` it will include `max_len` characters if
+ * the source is longer than `max_len`, but unlike `strncpy` it will
+ * always add zero termination.
+ */
+flatcc_builder_ref_t flatcc_builder_create_string_strn(flatcc_builder_t *B, const char *s, size_t max_len);
+
+/**
+ * Starts an empty string that can be extended subsequently.
+ *
+ * While the string is being created, it is guaranteed that there is
+ * always a null character after the end of the current string length.
+ * This also holds after `extend` and `append` operations. It is not
+ * allowed to modify the null character.
+ *
+ * Returns 0 on success.
+ */
+int flatcc_builder_start_string(flatcc_builder_t *B);
+
+/**
+ * Similar to `extend_vector` except for the buffer return type and a
+ * slight speed advantage. Strings are expected to contain utf-8 content
+ * but this isn't verified, and null characters would be accepted. The
+ * length is given in bytes.
+ *
+ * Appending too much, then truncating can be used to trim string
+ * escapes during parsing, or convert between unicode formats etc.
+ */
+char *flatcc_builder_extend_string(flatcc_builder_t *B, size_t len);
+
+/**
+ * Concatenes a length of string. If the string contains zeroes (which
+ * it formally shouldn't), they will be copied in.
+ *
+ * Returns the buffer holding a modifiable copy of the added content,
+ * or null on error.
+ */
+char *flatcc_builder_append_string(flatcc_builder_t *B, const char *s, size_t len);
+
+/** `append_string` up to zero termination of source. */
+char *flatcc_builder_append_string_str(flatcc_builder_t *B, const char *s);
+
+/** `append_string` up zero termination or at most max_len of source. */
+char *flatcc_builder_append_string_strn(flatcc_builder_t *B, const char *s, size_t max_len);
+
+/**
+ * Similar to `truncate_vector` available for consistency and a slight
+ * speed advantage. Reduces string by `len` bytes - it does not set
+ * the length. The resulting length must not become negative. Zero
+ * termination is not counted.
+ *
+ * Returns -1 of the length becomes negative, 0 on success.
+ */
+int flatcc_builder_truncate_string(flatcc_builder_t *B, size_t len);
+
+/**
+ * Similar to `end_vector` but adds a trailing zero not included
+ * in the length. The trailing zero is added regardless of whatever
+ * zero content may exist in the provided string (although it
+ * formally should not contain any).
+ */
+flatcc_builder_ref_t flatcc_builder_end_string(flatcc_builder_t *B);
+
+/** Returns the length of string currently on the stack. */
+size_t flatcc_builder_string_len(flatcc_builder_t *B);
+
+/**
+ * Returns a ponter to the start of the string
+ * accessible up the length of string currently on the stack.
+ */
+char *flatcc_builder_string_edit(flatcc_builder_t *B);
+
+
+/*
+ * Only for use with the default emitter.
+ *
+ * Fast acces to small buffers from default emitter.
+ *
+ * Only valid for default emitters before `flatcc_builder_clear`. The
+ * return buffer is not valid after a call to `flatcc_builder_reset` or
+ * `flatcc_builder_clear`.
+ *
+ * Returns null if the buffer size is too large to a have a linear
+ * memory representation or if the emitter is not the default. A valid
+ * size is between half and a full emitter page size depending on vtable
+ * content.
+ *
+ * Non-default emitters must be accessed by means specific to the
+ * particular emitter.
+ *
+ * If `size_out` is not null, it is set to the buffer size, or 0 if
+ * operation failed.
+ *
+ * The returned buffer should NOT be deallocated explicitly.
+ *
+ * The buffer size is the size reported by `flatcc_builder_get_buffer_size`.
+ */
+void *flatcc_builder_get_direct_buffer(flatcc_builder_t *B, size_t *size_out);
+
+/*
+ * Only for use with the default emitter.
+ *
+ * Default finalizer that allocates a buffer from the default emitter.
+ *
+ * Returns null if memory could not be allocated or if the emitter is
+ * not the default. This is just a convenience method - there are many
+ * other possible ways to extract the result of the emitter depending on
+ * use case.
+ *
+ * If `size_out` is not null, it is set to the buffer size, or 0 if
+ * operation failed.
+ *
+ * The allocated buffer is aligned according to malloc which may not be
+ * sufficient in advanced cases - for that purpose
+ * `flatcc_builder_finalize_aligned_buffer` may be used.
+ *
+ * It may be worth calling `flatcc_builder_get_direct_buffer` first to see
+ * if the buffer is small enough to avoid copying.
+ *
+ * The returned buffer must be deallocated using `free`.
+ */
+void *flatcc_builder_finalize_buffer(flatcc_builder_t *B, size_t *size_out);
+
+/*
+ * Only for use with the default emitter.
+ *
+ * Similar to `flatcc_builder_finalize_buffer` but ensures the returned
+ * memory is aligned to the overall alignment required for the buffer.
+ * Often it is not necessary unless special operations rely on larger
+ * alignments than the stored scalars.
+ *
+ * If `size_out` is not null, it is set to the buffer size, or 0 if
+ * operation failed.
+ *
+ * The returned buffer must be deallocated using `aligned_free` which is
+ * implemented via `flatcc_flatbuffers.h`. `free` will usually work but
+ * is not portable to platforms without posix_memalign or C11
+ * aligned_alloc support.
+ */
+void *flatcc_builder_finalize_aligned_buffer(flatcc_builder_t *B, size_t *size_out);
+
+/*
+ * Only for use with the default emitter.
+ *
+ * Convenience method to copy buffer from default emitter. Forwards
+ * call to default emitter and returns input pointer, or null if
+ * the emitter is not default or of the given size is smaller than
+ * the buffer size.
+ *
+ * Note: the `size` argument is the target buffers capacity, not the
+ * flatcc_builders buffer size.
+ *
+ * Other emitters have custom interfaces for reaching their content.
+ */
+void *flatcc_builder_copy_buffer(flatcc_builder_t *B, void *buffer, size_t size);
+
+#endif /* FLATCC_BUILDER_H */
diff --git a/Source/library/FBSUtil/flatcc/flatcc_emitter.h b/Source/library/FBSUtil/flatcc/flatcc_emitter.h
new file mode 100644
index 0000000..af9a5af
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/flatcc_emitter.h
@@ -0,0 +1,198 @@
+#ifndef FLATCC_EMITTER_H
+#define FLATCC_EMITTER_H
+
+/*
+ * Default implementation of a flatbuilder emitter.
+ *
+ * This may be used as a starting point for more advanced emitters,
+ * for example writing completed pages to disk or network and
+ * the recycling those pages.
+ */
+
+#include
+#include
+
+#include "flatcc/flatcc_types.h"
+#include "flatcc/flatcc_iov.h"
+
+/*
+ * The buffer steadily grows during emission but the design allows for
+ * an extension where individual pages can recycled before the buffer
+ * is complete, for example because they have been transmitted.
+ *
+ * When done, the buffer can be cleared to free all memory, or reset to
+ * maintain an adaptive page pool for next buffer construction.
+ *
+ * Unlike an exponentially growing buffer, each buffer page remains
+ * stable in memory until reset, clear or recycle is called.
+ *
+ * Design notes for possible extensions:
+ *
+ * The buffer is a ring buffer marked by a front and a back page. The
+ * front and back may be the same page and may initially be absent.
+ * Anything outside these pages are unallocated pages for recycling.
+ * Any page between (but excluding) the front and back pages may be
+ * recycled by unlinking and relinking outside the front and back pages
+ * but then copy operations no longer makes sense. Each page stores the
+ * logical offset within the buffer but isn't otherwise used by the
+ * implemention - it might be used for network transmission. The buffer
+ * is not explicitly designed for multithreaded access but any page
+ * strictly between front and back is not touched unless recycled and in
+ * this case aligned allocation is useful to prevent cache line sharing.
+ */
+
+/*
+ * Memory is allocated in fixed size page units - the first page is
+ * split between front and back so each get half the page size. If the
+ * size is a multiple of 128 then each page offset will be a multiple of
+ * 64, which may be useful for sequencing etc.
+ */
+#ifndef FLATCC_EMITTER_PAGE_SIZE
+#define FLATCC_EMITTER_MAX_PAGE_SIZE 3000
+#define FLATCC_EMITTER_PAGE_MULTIPLE 64
+#define FLATCC_EMITTER_PAGE_SIZE ((FLATCC_EMITTER_MAX_PAGE_SIZE) &\
+ ~(2 * (FLATCC_EMITTER_PAGE_MULTIPLE) - 1))
+#endif
+
+#ifndef FLATCC_EMITTER_ALLOC
+#ifdef FLATCC_EMITTER_USE_ALIGNED_ALLOC
+/*
+ * does not always provide aligned_alloc, so include whatever
+ * is required when enabling this feature.
+ */
+#define FLATCC_EMITTER_ALLOC(n) aligned_alloc(FLATCC_EMITTER_PAGE_MULTIPLE,\
+ (((n) + FLATCC_EMITTER_PAGE_MULTIPLE - 1) & ~(FLATCC_EMITTER_PAGE_MULTIPLE - 1)))
+#else
+#define FLATCC_EMITTER_ALLOC malloc
+#endif
+#endif
+
+typedef struct flatcc_emitter_page flatcc_emitter_page_t;
+typedef struct flatcc_emitter flatcc_emitter_t;
+
+struct flatcc_emitter_page {
+ uint8_t page[FLATCC_EMITTER_PAGE_SIZE];
+ flatcc_emitter_page_t *next;
+ flatcc_emitter_page_t *prev;
+ /*
+ * The offset is relative to page start, but not necessarily
+ * to any present content if part of front or back page,
+ * and undefined for unused pages.
+ */
+ flatbuffers_soffset_t page_offset;
+};
+
+/*
+ * Must be allocated and zeroed externally, e.g. on the stack
+ * then provided as emit_context to the flatbuilder along
+ * with the `flatcc_emitter` function.
+ */
+struct flatcc_emitter {
+ flatcc_emitter_page_t *front, *back;
+ uint8_t *front_cursor;
+ size_t front_left;
+ uint8_t *back_cursor;
+ size_t back_left;
+ size_t used;
+ size_t capacity;
+ size_t used_average;
+};
+
+/* Optional helper to ensure emitter is zeroed initially. */
+static inline void flatcc_emitter_init(flatcc_emitter_t *E)
+{
+ memset(E, 0, sizeof(*E));
+}
+
+/* Deallocates all buffer memory making the emitter ready for next use. */
+void flatcc_emitter_clear(flatcc_emitter_t *E);
+
+/*
+ * Similar to `clear_flatcc_emitter` but heuristacally keeps some allocated
+ * memory between uses while gradually reducing peak allocations.
+ * For small buffers, a single page will remain available with no
+ * additional allocations or deallocations after first use.
+ */
+void flatcc_emitter_reset(flatcc_emitter_t *E);
+
+/*
+ * Helper function that allows a page between front and back to be
+ * recycled while the buffer is still being constructed - most likely as part
+ * of partial copy or transmission. Attempting to recycle front or back
+ * pages will result will result in an error. Recycling pages outside the
+ * front and back will be valid but pointless. After recycling and copy
+ * operations are no longer well-defined and should be replaced with
+ * whatever logic is recycling the pages. The reset operation
+ * automatically recycles all (remaining) pages when emission is
+ * complete. After recycling, the `flatcc_emitter_size` function will
+ * return as if recycle was not called, but will only represent the
+ * logical size, not the size of the active buffer. Because a recycled
+ * page is fully utilized, it is fairly easy to compensate for this if
+ * required.
+ *
+ * Returns 0 on success.
+ */
+int flatcc_emitter_recycle_page(flatcc_emitter_t *E, flatcc_emitter_page_t *p);
+
+/*
+ * The amount of data copied with `flatcc_emitter_copy_buffer` and related
+ * functions. Normally called at end of buffer construction but is
+ * always valid, as is the copy functions. The size is a direct
+ * function of the amount emitted data so the flatbuilder itself can
+ * also provide this information.
+ */
+static inline size_t flatcc_emitter_get_buffer_size(flatcc_emitter_t *E)
+{
+ return E->used;
+}
+
+/*
+ * Returns buffer start iff the buffer fits on a single internal page.
+ * Only useful for fairly small buffers - about half the page size since
+ * one half of first page goes to vtables that likely use little space.
+ * Returns null if request could not be served.
+ *
+ * If `size_out` is not null, it is set to the buffer size, or 0 if
+ * operation failed.
+ */
+static inline void *flatcc_emitter_get_direct_buffer(flatcc_emitter_t *E, size_t *size_out)
+{
+ if (E->front == E->back) {
+ if (size_out) {
+ *size_out = E->used;
+ }
+ return E->front_cursor;
+ }
+ if (size_out) {
+ *size_out = 0;
+ }
+ return 0;
+}
+
+/*
+ * Copies the internal flatcc_emitter representation to an externally
+ * provided linear buffer that must have size `flatcc_emitter_get_size`.
+ *
+ * If pages have been recycled, only the remaining pages will be copied
+ * and thus less data than what `flatcc_emitter_get_size` would suggest. It
+ * makes more sense to provide a customized copy operation when
+ * recycling pages.
+ *
+ * If the buffer is too small, nothing is copied, otherwise the
+ * full buffer is copied and the input buffer is returned.
+ */
+void *flatcc_emitter_copy_buffer(flatcc_emitter_t *E, void *buf, size_t size);
+
+/*
+ * The emitter interface function to the flatbuilder API.
+ * `emit_context` should be of type `flatcc_emitter_t` for this
+ * particular implementation.
+ *
+ * This function is compatible with the `flatbuilder_emit_fun`
+ * type defined in "flatbuilder.h".
+ */
+int flatcc_emitter(void *emit_context,
+ const flatcc_iovec_t *iov, int iov_count,
+ flatbuffers_soffset_t offset, size_t len);
+
+#endif /* FLATCC_EMITTER_H */
diff --git a/Source/library/FBSUtil/flatcc/flatcc_endian.h b/Source/library/FBSUtil/flatcc/flatcc_endian.h
new file mode 100644
index 0000000..ffef503
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/flatcc_endian.h
@@ -0,0 +1,115 @@
+#ifndef FLATCC_ENDIAN_H
+#define FLATCC_ENDIAN_H
+
+/*
+ * This file provides helper macros to define type-specific macros and
+ * inline functions that convert between stored data and native data
+ * indedpently of both native (host) endianness and protocol endianness
+ * (i.e. the serialized endian format).
+ *
+ * To detect endianness correctly ensure one of the following is defined.
+ *
+ * __LITTLE_ENDIAN__
+ * __BIG_ENDIAN__
+ * FLATBUFFERS_LITTLEENDIAN=1
+ * FLATBUFFERS_LITTLEENDIAN=0
+ *
+ * Note: the Clang compiler likely already does this, but other
+ * compilers may have their own way, if at all.
+ *
+ * It is also necessary to include or a compatible
+ * implementation in order to provide:
+ *
+ * le16toh, le32to, le64toh, be16toh, be32toh, be64toh,
+ * htole16, htole32, htole64, htobe16, htobe32, htobe64.
+ *
+ * A simple way to ensure all of the above for most platforms is
+ * to include the portable endian support file:
+ *
+ * #include "flatcc/portable/pendian.h"
+ *
+ * It is also necessary to include
+ *
+ * #include "flatcc/flatcc_types.h"
+ *
+ * or an equivalent file. This makes it possible to change the
+ * endianness of the serialized data and the sizes of flatbuffer
+ * specific types such as `uoffset_t`.
+ *
+ * Note: the mentioned include files are likely already included
+ * by the file including this file, at least for the default
+ * configuration.
+ */
+
+#ifndef UINT8_t
+#include
+#endif
+
+/* These are needed to simplify accessor macros and are not found in . */
+#ifndef le8toh
+#define le8toh(n) (n)
+#endif
+
+#ifndef be8toh
+#define be8toh(n) (n)
+#endif
+
+#ifndef htole8
+#define htole8(n) (n)
+#endif
+
+#ifndef htobe8
+#define htobe8(n) (n)
+#endif
+
+#include "flatcc/flatcc_accessors.h"
+
+/* This is the binary encoding endianness, usually LE for flatbuffers. */
+#if FLATBUFFERS_PROTOCOL_IS_LE
+#define flatbuffers_endian le
+#elif FLATBUFFERS_PROTOCOL_IS_BE
+#define flatbuffers_endian be
+#else
+#error "flatbuffers has no defined endiannesss"
+#endif
+
+ __flatcc_define_basic_scalar_accessors(flatbuffers_, flatbuffers_endian)
+
+ __flatcc_define_integer_accessors(flatbuffers_bool, flatbuffers_bool_t,
+ FLATBUFFERS_BOOL_WIDTH, flatbuffers_endian)
+
+ __flatcc_define_integer_accessors(__flatbuffers_uoffset, flatbuffers_uoffset_t,
+ FLATBUFFERS_UOFFSET_WIDTH, flatbuffers_endian)
+ __flatcc_define_integer_accessors(__flatbuffers_soffset, flatbuffers_soffset_t,
+ FLATBUFFERS_SOFFSET_WIDTH, flatbuffers_endian)
+ __flatcc_define_integer_accessors(__flatbuffers_voffset, flatbuffers_voffset_t,
+ FLATBUFFERS_VOFFSET_WIDTH, flatbuffers_endian)
+ __flatcc_define_integer_accessors(__flatbuffers_utype, flatbuffers_utype_t,
+ FLATBUFFERS_UTYPE_WIDTH, flatbuffers_endian)
+ __flatcc_define_integer_accessors(__flatbuffers_thash, flatbuffers_thash_t,
+ FLATBUFFERS_THASH_WIDTH, flatbuffers_endian)
+
+/* flatcc/portable/pendian.h sets LITTLE/BIG flags if possible, and always defines le16toh. */
+#ifndef flatbuffers_is_native_pe
+#if defined(__LITTLE_ENDIAN__) || FLATBUFFERS_LITTLEENDIAN
+#undef FLATBUFFERS_LITTLEENDIAN
+#define FLATBUFFERS_LITTLEENDIAN 1
+#define flatbuffers_is_native_pe() (FLATBUFFERS_PROTOCOL_IS_LE)
+#elif defined(__BIG_ENDIAN__) || (defined(FLATBUFFERS_LITTLEENDIAN) && !FLATBUFFERS_LITTLEENDIAN)
+#undef FLATBUFFERS_LITTLEENDIAN
+#define FLATBUFFERS_LITTLEENDIAN 0
+#define flatbuffers_is_native_pe() (FLATBUFFERS_PROTOCOL_IS_BE)
+#else
+#define flatbuffers_is_native_pe() (__FLATBUFFERS_CONCAT(flatbuffers_endian, 16toh)(1) == 1)
+#endif
+#endif
+
+#ifndef flatbuffers_is_native_le
+#define flatbuffers_is_native_le() flatbuffers_is_native_pe()
+#endif
+
+#ifndef flatbuffers_is_native_be
+#define flatbuffers_is_native_be() (!flatbuffers_is_native_pe())
+#endif
+
+#endif /* FLATCC_ENDIAN_H */
diff --git a/Source/library/FBSUtil/flatcc/flatcc_flatbuffers.h b/Source/library/FBSUtil/flatcc/flatcc_flatbuffers.h
new file mode 100644
index 0000000..071ad89
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/flatcc_flatbuffers.h
@@ -0,0 +1,48 @@
+/*
+ * Even C11 compilers depend on clib support for `static_assert` which
+ * isn't always present, so we deal with this here for all compilers.
+ *
+ * Outside include guard to handle scope counter.
+ */
+#include "flatcc/portable/pstatic_assert.h"
+
+#ifndef FLATCC_FLATBUFFERS_H
+#define FLATCC_FLATBUFFERS_H
+
+#ifndef flatcc_flatbuffers_defined
+#define flatcc_flatbuffers_defined
+
+#ifdef FLATCC_PORTABLE
+#include "flatcc/flatcc_portable.h"
+#endif
+#include "flatcc/portable/pwarnings.h"
+/* Needed by C99 compilers without FLATCC_PORTABLE. */
+#include "flatcc/portable/pstdalign.h"
+
+/*
+ * Implements `aligned_alloc` and `aligned_free`.
+ * Even with C11, this implements non-standard aligned_free needed for portable
+ * aligned_alloc implementations.
+ */
+#include "flatcc/portable/paligned_alloc.h"
+
+#define __FLATBUFFERS_PASTE2(a, b) a ## b
+#define __FLATBUFFERS_PASTE3(a, b, c) a ## b ## c
+#define __FLATBUFFERS_CONCAT(a, b) __FLATBUFFERS_PASTE2(a, b)
+
+/*
+ * "flatcc_endian.h" requires the preceeding include files,
+ * or compatible definitions.
+ */
+#include "flatcc/portable/pendian.h"
+#include "flatcc/flatcc_types.h"
+#include "flatcc/flatcc_endian.h"
+#include "flatcc/flatcc_identifier.h"
+
+#ifndef FLATBUFFERS_WRAP_NAMESPACE
+#define FLATBUFFERS_WRAP_NAMESPACE(ns, x) ns ## _ ## x
+#endif
+
+#endif /* flatcc_flatbuffers_defined */
+
+#endif /* FLATCC_FLATBUFFERS_H */
diff --git a/Source/library/FBSUtil/flatcc/flatcc_identifier.h b/Source/library/FBSUtil/flatcc/flatcc_identifier.h
new file mode 100644
index 0000000..fcceb34
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/flatcc_identifier.h
@@ -0,0 +1,115 @@
+#ifndef FLATCC_IDENTIFIER_H
+#define FLATCC_IDENTIFIER_H
+
+#ifndef FLATCC_FLATBUFFERS_H
+#error "include via flatcc/flatcc_flatbuffers.h"
+#endif
+
+/*
+ * FlatBuffers identifiers are normally specified by "file_identifer" in
+ * the schema, but a standard hash of the fully qualified type name can
+ * also be used. This file implements such a mapping, but the generated
+ * headers also contain the necessary information for known types.
+ */
+
+
+/*
+ * Returns the type hash of a given name in native endian format.
+ * Generated code already provides these, but if a name was changed
+ * in the schema it may be relevant to recompute the hash manually.
+ *
+ * The wire-format of this value should always be little endian.
+ *
+ * Note: this must be the fully qualified name, e.g. in the namespace
+ * "MyGame.Example":
+ *
+ * flatbuffers_type_hash_from_name("MyGame.Example.Monster");
+ *
+ * or, in the global namespace just:
+ *
+ * flatbuffers_type_hash_from_name("MyTable");
+ *
+ * This assumes 32 bit hash type. For other sizes, other FNV-1a
+ * constants would be required.
+ *
+ * Note that we reserve hash value 0 for missing or ignored value.
+ */
+static inline flatbuffers_thash_t flatbuffers_type_hash_from_name(const char *name)
+{
+ uint32_t hash = 2166136261UL;
+ while (*name) {
+ hash ^= (uint32_t)*name;
+ hash = hash * 16777619UL;
+ ++name;
+ }
+ if (hash == 0) {
+ hash = 2166136261UL;
+ }
+ return hash;
+}
+
+/*
+ * Type hash encoded as little endian file identifier string.
+ * Note: if type hash is 0, the identifier should be null which
+ * we cannot return in this interface.
+ */
+static inline void flatbuffers_identifier_from_type_hash(flatbuffers_thash_t type_hash, flatbuffers_fid_t out_identifier)
+{
+ out_identifier[0] = type_hash & 0xff;
+ type_hash >>= 8;
+ out_identifier[1] = type_hash & 0xff;
+ type_hash >>= 8;
+ out_identifier[2] = type_hash & 0xff;
+ type_hash >>= 8;
+ out_identifier[3] = type_hash & 0xff;
+}
+
+/* Native integer encoding of file identifier. */
+static inline flatbuffers_thash_t flatbuffers_type_hash_from_identifier(const flatbuffers_fid_t identifier)
+{
+ uint8_t *p = (uint8_t *)identifier;
+
+ return identifier ?
+ (uint32_t)p[0] + (((uint32_t)p[1]) << 8) + (((uint32_t)p[2]) << 16) + (((uint32_t)p[3]) << 24) : 0;
+}
+
+/*
+ * Computes the little endian wire format of the type hash. It can be
+ * used as a file identifer argument to various flatcc buffer calls.
+ *
+ * `flatbuffers_fid_t` is just `char [4]` for the default flatbuffers
+ * type system defined in `flatcc/flatcc_types.h`.
+ */
+static inline void flatbuffers_identifier_from_name(const char *name, flatbuffers_fid_t out_identifier)
+{
+ flatbuffers_identifier_from_type_hash(flatbuffers_type_hash_from_name(name), out_identifier);
+}
+
+/*
+ * This is a collision free hash (a permutation) of the type hash to
+ * provide better distribution for use in hash tables. It is likely not
+ * necessary in praxis, and for uniqueness of identifiers it provides no
+ * advantage over just using the FNV-1a type hash, except when truncating
+ * the identifier to less than 32-bits.
+ *
+ * Note: the output should not be used in transmission. It provides no
+ * additional information and just complicates matters. Furthermore, the
+ * unmodified type hash has the benefit that it can seed a child namespace.
+ */
+static inline uint32_t flatbuffers_disperse_type_hash(flatbuffers_thash_t type_hash)
+{
+ /* http://stackoverflow.com/a/12996028 */
+ uint32_t x = type_hash;
+
+ x = ((x >> 16) ^ x) * 0x45d9f3bUL;
+ x = ((x >> 16) ^ x) * 0x45d9f3bUL;
+ x = ((x >> 16) ^ x);
+ return x;
+}
+
+
+/* We have hardcoded assumptions about identifier size. */
+static_assert(sizeof(flatbuffers_fid_t) == 4, "unexpected file identifier size");
+static_assert(sizeof(flatbuffers_thash_t) == 4, "unexpected type hash size");
+
+#endif /* FLATCC_IDENTIFIER_H */
diff --git a/Source/library/FBSUtil/flatcc/flatcc_iov.h b/Source/library/FBSUtil/flatcc/flatcc_iov.h
new file mode 100644
index 0000000..858e2de
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/flatcc_iov.h
@@ -0,0 +1,23 @@
+#ifndef FLATCC_IOV_H
+#define FLATCC_IOV_H
+
+#include
+
+/*
+ * The emitter receives one, or a few buffers at a time via
+ * this type. compatible iovec structure used for
+ * allocation and emitter interface.
+ */
+typedef struct flatcc_iovec flatcc_iovec_t;
+struct flatcc_iovec {
+ void *iov_base;
+ size_t iov_len;
+};
+
+/*
+ * The largest iovec vector the builder will issue. It will
+ * always be a relatively small number.
+ */
+#define FLATCC_IOV_COUNT_MAX 8
+
+#endif /* FLATCC_IOV_H */
diff --git a/Source/library/FBSUtil/flatcc/flatcc_json_parser.h b/Source/library/FBSUtil/flatcc/flatcc_json_parser.h
new file mode 100644
index 0000000..69b9a3c
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/flatcc_json_parser.h
@@ -0,0 +1,793 @@
+#ifndef FLATCC_JSON_PARSE_H
+#define FLATCC_JSON_PARSE_H
+
+/*
+ * JSON RFC:
+ * http://www.ietf.org/rfc/rfc4627.txt?number=4627
+ *
+ * With several flatbuffers specific extensions.
+ */
+
+#include
+#include
+#include
+
+#include "flatcc/flatcc_rtconfig.h"
+#include "flatcc/flatcc_builder.h"
+#include "flatcc/flatcc_unaligned.h"
+
+#define PDIAGNOSTIC_IGNORE_UNUSED
+#include "flatcc/portable/pdiagnostic_push.h"
+
+enum flatcc_json_parser_flags {
+ flatcc_json_parser_f_skip_unknown = 1,
+ flatcc_json_parser_f_force_add = 2
+};
+
+#define FLATCC_JSON_PARSE_ERROR_MAP(XX) \
+ XX(ok, "ok") \
+ XX(eof, "eof") \
+ XX(deep_nesting, "deep nesting") \
+ XX(trailing_comma, "trailing comma") \
+ XX(expected_colon, "expected colon") \
+ XX(unexpected_character, "unexpected character") \
+ XX(invalid_numeric, "invalid numeric") \
+ XX(overflow, "overflow") \
+ XX(underflow, "underflow") \
+ XX(unbalanced_array, "unbalanced array") \
+ XX(unbalanced_object, "unbalanced object") \
+ XX(precision_loss, "precision loss") \
+ XX(float_unexpected, "float unexpected") \
+ XX(unknown_symbol, "unknown symbol") \
+ XX(unquoted_symbolic_list, "unquoted list of symbols") \
+ XX(unknown_union, "unknown union type") \
+ XX(expected_string, "expected string") \
+ XX(invalid_character, "invalid character") \
+ XX(invalid_escape, "invalid escape") \
+ XX(invalid_type, "invalid type") \
+ XX(unterminated_string, "unterminated string") \
+ XX(expected_object, "expected object") \
+ XX(expected_array, "expected array") \
+ XX(expected_scalar, "expected literal or symbolic scalar") \
+ XX(expected_union_type, "expected union type") \
+ XX(union_none, "union is NONE") \
+ XX(union_incomplete, "table has incomplete union") \
+ XX(duplicate, "table has duplicate field") \
+ XX(required, "required field missing") \
+ XX(runtime, "runtime error") \
+ XX(not_supported, "not supported")
+
+enum flatcc_json_parser_error_no {
+#define XX(no, str) flatcc_json_parser_error_##no,
+ FLATCC_JSON_PARSE_ERROR_MAP(XX)
+#undef XX
+};
+
+const char *flatcc_json_parser_error_string(int err);
+
+#define flatcc_json_parser_ok flatcc_json_parser_error_ok
+#define flatcc_json_parser_eof flatcc_json_parser_error_eof
+
+/*
+ * The struct may be zero initialized in which case the line count will
+ * start at line zero, or the line may be set to 1 initially. The ctx
+ * is only used for error reporting and tracking non-standard unquoted
+ * ctx.
+ *
+ * `ctx` may for example hold a flatcc_builder_t pointer.
+ */
+typedef struct flatcc_json_parser_ctx flatcc_json_parser_t;
+struct flatcc_json_parser_ctx {
+ flatcc_builder_t *ctx;
+ const char *line_start;
+ int flags;
+#if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
+ int unquoted;
+#endif
+
+ int line, pos;
+ int error;
+ const char *start;
+ const char *end;
+ const char *error_loc;
+ /* Set at end of successful parse. */
+ const char *end_loc;
+};
+
+static inline int flatcc_json_parser_get_error(flatcc_json_parser_t *ctx)
+{
+ return ctx->error;
+}
+
+static inline void flatcc_json_parser_init(flatcc_json_parser_t *ctx, flatcc_builder_t *B, const char *buf, const char *end, int flags)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->ctx = B;
+ ctx->line_start = buf;
+ ctx->line = 1;
+ ctx->flags = flags;
+ /* These are not needed for parsing, but may be helpful in reporting etc. */
+ ctx->start = buf;
+ ctx->end = end;
+ ctx->error_loc = buf;
+}
+
+const char *flatcc_json_parser_set_error(flatcc_json_parser_t *ctx, const char *loc, const char *end, int reason);
+
+/*
+ * Wide space is not necessarily beneficial in the typical space, but it
+ * also isn't expensive so it may be added when there are applications
+ * that can benefit.
+ */
+const char *flatcc_json_parser_space_ext(flatcc_json_parser_t *ctx, const char *buf, const char *end);
+
+static inline const char *flatcc_json_parser_space(flatcc_json_parser_t *ctx, const char *buf, const char *end)
+{
+ if (end - buf > 1) {
+ if (buf[0] > 0x20) {
+ return buf;
+ }
+ if (buf[0] == 0x20 && buf[1] > 0x20) {
+ return buf + 1;
+ }
+ }
+ return flatcc_json_parser_space_ext(ctx, buf, end);
+}
+
+
+static inline const char *flatcc_json_parser_string_start(flatcc_json_parser_t *ctx, const char *buf, const char *end)
+{
+ if (buf == end || *buf != '\"') {
+ return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_expected_string);
+ }
+ return ++buf;
+}
+
+static inline const char *flatcc_json_parser_string_end(flatcc_json_parser_t *ctx, const char *buf, const char *end)
+{
+ if (buf == end || *buf != '\"') {
+ return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unterminated_string);
+ }
+ return ++buf;
+}
+
+/*
+ * Creates a string. Returns *ref == 0 on unrecoverable error or
+ * sets *ref to a valid new string reference.
+ */
+const char *flatcc_json_parser_build_string(flatcc_json_parser_t *ctx,
+ const char *buf, const char *end, flatcc_builder_ref_t *ref);
+
+typedef char flatcc_json_parser_escape_buffer_t[4];
+/*
+ * If the buffer does not hold a valid escape sequence, an error is
+ * returned with code[0] = 0/
+ *
+ * Otherwise code[0] the length (1-3) of the remaining
+ * characters in the code, transcoded from the escape sequence.
+ *
+ * The JSON extension `\xXX` is supported and may produced invalid UTF-8
+ * characters such as 0xff. The standard JSON escape `\uXXXX` is not
+ * checked for invalid code points and may produce invlalid UTF-8.
+ *
+ * Regular characters are expected to valid UTF-8 but they are not checked
+ * and may therefore produce invalid UTF-8.
+ *
+ * Control characters within a string are rejected except in the
+ * standard JSON escpaped form for `\n \r \t \b \f`.
+ *
+ * Additional escape codes as per standard JSON: `\\ \/ \"`.
+ */
+const char *flatcc_json_parser_string_escape(flatcc_json_parser_t *ctx, const char *buf, const char *end, flatcc_json_parser_escape_buffer_t code);
+
+/*
+ * Parses the longest unescaped run of string content followed by either
+ * an escape encoding, string termination, or error.
+ */
+const char *flatcc_json_parser_string_part(flatcc_json_parser_t *ctx, const char *buf, const char *end);
+
+static inline const char *flatcc_json_parser_symbol_start(flatcc_json_parser_t *ctx, const char *buf, const char *end)
+{
+ if (buf == end) {
+ return buf;
+ }
+ if (*buf == '\"') {
+ ++buf;
+#if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
+ ctx->unquoted = 0;
+#endif
+ } else {
+#if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
+ if (*buf == '.') {
+ return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unexpected_character);
+ }
+ ctx->unquoted = 1;
+#else
+ return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unexpected_character);
+#endif
+ }
+ return buf;
+}
+
+static inline uint64_t flatcc_json_parser_symbol_part_ext(const char *buf, const char *end)
+{
+ uint64_t w = 0;
+ size_t n = end - buf;
+
+ if (n > 8) {
+ n = 8;
+ }
+ /* This can bloat inlining for a rarely executed case. */
+#if 1
+ switch (n) {
+ case 8: w |= ((uint64_t)buf[7]) << (0 * 8);
+ case 7: w |= ((uint64_t)buf[6]) << (1 * 8);
+ case 6: w |= ((uint64_t)buf[5]) << (2 * 8);
+ case 5: w |= ((uint64_t)buf[4]) << (3 * 8);
+ case 4: w |= ((uint64_t)buf[3]) << (4 * 8);
+ case 3: w |= ((uint64_t)buf[2]) << (5 * 8);
+ case 2: w |= ((uint64_t)buf[1]) << (6 * 8);
+ case 1: w |= ((uint64_t)buf[0]) << (7 * 8);
+ case 0:
+ break;
+ }
+#else
+ /* But this is hardly much of an improvement. */
+ {
+ size_t i;
+ for (i = 0; i < n; ++i) {
+ w <<= 8;
+ if (i < n) {
+ w = buf[i];
+ }
+ }
+ }
+#endif
+ return w;
+}
+
+/*
+ * Read out string as a big endian word. This allows for trie lookup,
+ * also when trailing characters are beyond keyword. This assumes the
+ * external words tested against are valid and therefore there need be
+ * no checks here. If a match is not made, the symbol_end function will
+ * consume and check any unmatched content - from _before_ this function
+ * was called - i.e. the returned buffer is tentative for use only if we
+ * accept the part returned here.
+ *
+ * Used for both symbols and symbolic constants.
+ */
+static inline uint64_t flatcc_json_parser_symbol_part(const char *buf, const char *end)
+{
+ size_t n = end - buf;
+
+#if FLATCC_ALLOW_UNALIGNED_ACCESS
+ if (n >= 8) {
+ return be64toh(*(uint64_t *)buf);
+ }
+#endif
+ return flatcc_json_parser_symbol_part_ext(buf, end);
+}
+
+/* Don't allow space in dot notation neither inside nor outside strings. */
+static inline const char *flatcc_json_parser_match_scope(flatcc_json_parser_t *ctx, const char *buf, const char *end, int pos)
+{
+ const char *mark = buf;
+
+ (void)ctx;
+
+ if (end - buf <= pos) {
+ return mark;
+ }
+ if (buf[pos] != '.') {
+ return mark;
+ }
+ return buf + pos + 1;
+}
+
+const char *flatcc_json_parser_match_constant(flatcc_json_parser_t *ctx, const char *buf, const char *end, int pos, int *more);
+
+/* We allow '.' in unquoted symbols, but not at the start or end. */
+static inline const char *flatcc_json_parser_symbol_end(flatcc_json_parser_t *ctx, const char *buf, const char *end)
+{
+ char c, clast = 0;
+
+
+#if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
+ if (ctx->unquoted) {
+ while (buf != end && *buf > 0x20) {
+ clast = c = *buf;
+ if (c == '_' || c == '.' || (c & 0x80) || (c >= '0' && c <= '9')) {
+ ++buf;
+ continue;
+ }
+ /* Lower case. */
+ c |= 0x20;
+ if (c >= 'a' && c <= 'z') {
+ ++buf;
+ continue;
+ }
+ break;
+ }
+ if (clast == '.') {
+ return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unexpected_character);
+ }
+ } else {
+#else
+ {
+#endif
+ while (buf != end && *buf != '\"') {
+ if (*buf == '\\') {
+ if (end - buf < 2) {
+ break;
+ }
+ ++buf;
+ }
+ ++buf;
+ }
+ if (buf == end || *buf != '\"') {
+ return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unterminated_string);
+ }
+ ++buf;
+ }
+ return buf;
+}
+
+static inline const char *flatcc_json_parser_constant_start(flatcc_json_parser_t *ctx, const char *buf, const char *end)
+{
+ buf = flatcc_json_parser_symbol_start(ctx, buf, end);
+#if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
+ if (!ctx->unquoted) {
+#else
+ {
+#endif
+ buf = flatcc_json_parser_space(ctx, buf, end);
+ }
+ return buf;
+}
+
+static inline const char *flatcc_json_parser_object_start(flatcc_json_parser_t *ctx, const char *buf, const char *end, int *more)
+{
+ if (buf == end || *buf != '{') {
+ *more = 0;
+ return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_expected_object);
+ }
+ buf = flatcc_json_parser_space(ctx, buf + 1, end);
+ if (buf != end && *buf == '}') {
+ *more = 0;
+ return flatcc_json_parser_space(ctx, buf + 1, end);
+ }
+ *more = 1;
+ return buf;
+}
+
+static inline const char *flatcc_json_parser_object_end(flatcc_json_parser_t *ctx, const char *buf,
+ const char *end, int *more)
+{
+ buf = flatcc_json_parser_space(ctx, buf, end);
+ if (buf == end) {
+ *more = 0;
+ return buf;
+ }
+ if (*buf != ',') {
+ *more = 0;
+ if (*buf != '}') {
+ return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unbalanced_object);
+ } else {
+ return flatcc_json_parser_space(ctx, buf + 1, end);
+ }
+ }
+ buf = flatcc_json_parser_space(ctx, buf + 1, end);
+ if (buf == end) {
+ *more = 0;
+ return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unbalanced_object);
+ }
+#if FLATCC_JSON_PARSE_ALLOW_TRAILING_COMMA
+ if (*buf == '}') {
+ *more = 0;
+ return flatcc_json_parser_space(ctx, buf + 1, end);
+ }
+#endif
+ *more = 1;
+ return buf;
+}
+
+static inline const char *flatcc_json_parser_array_start(flatcc_json_parser_t *ctx, const char *buf, const char *end, int *more)
+{
+ if (buf == end || *buf != '[') {
+ *more = 0;
+ return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_expected_array);
+ }
+ buf = flatcc_json_parser_space(ctx, buf + 1, end);
+ if (buf != end && *buf == ']') {
+ *more = 0;
+ return flatcc_json_parser_space(ctx, buf + 1, end);
+ }
+ *more = 1;
+ return buf;
+}
+
+static inline const char *flatcc_json_parser_array_end(flatcc_json_parser_t *ctx, const char *buf,
+ const char *end, int *more)
+{
+ buf = flatcc_json_parser_space(ctx, buf, end);
+ if (buf == end) {
+ *more = 0;
+ return buf;
+ }
+ if (*buf != ',') {
+ *more = 0;
+ if (*buf != ']') {
+ return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unbalanced_array);
+ } else {
+ return flatcc_json_parser_space(ctx, buf + 1, end);
+ }
+ }
+ buf = flatcc_json_parser_space(ctx, buf + 1, end);
+ if (buf == end) {
+ *more = 0;
+ return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unbalanced_array);
+ }
+#if FLATCC_JSON_PARSE_ALLOW_TRAILING_COMMA
+ if (*buf == ']') {
+ *more = 0;
+ return flatcc_json_parser_space(ctx, buf + 1, end);
+ }
+#endif
+ *more = 1;
+ return buf;
+}
+
+/*
+ * Detects if a symbol terminates at a given `pos` relative to the
+ * buffer pointer, or return fast.
+ *
+ * Failure to match is not an error but a recommendation to try
+ * alternative longer suffixes - only if such do not exist will
+ * there be an error. If a match was not eventually found,
+ * the `flatcc_json_parser_unmatched_symbol` should be called to consume
+ * the symbol and generate error messages.
+ *
+ * If a match was detected, ':' and surrounding space is consumed,
+ * or an error is generated.
+ */
+static inline const char *flatcc_json_parser_match_symbol(flatcc_json_parser_t *ctx, const char *buf,
+ const char *end, int pos)
+{
+ const char *mark = buf;
+
+ if (end - buf <= pos) {
+ return mark;
+ }
+#if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
+ if (ctx->unquoted) {
+ if (buf[pos] > 0x20 && buf[pos] != ':') {
+ return mark;
+ }
+ buf += pos;
+ ctx->unquoted = 0;
+ } else {
+#else
+ {
+#endif
+ if (buf[pos] != '\"') {
+ return mark;
+ }
+ buf += pos + 1;
+ }
+ buf = flatcc_json_parser_space(ctx, buf, end);
+ if (buf != end && *buf == ':') {
+ ++buf;
+ return flatcc_json_parser_space(ctx, buf, end);
+ }
+ return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_expected_colon);
+}
+
+static inline const char *flatcc_json_parser_match_type_suffix(flatcc_json_parser_t *ctx, const char *buf, const char *end, int pos)
+{
+ if (end - buf <= pos + 5) {
+ return buf;
+ }
+ if (memcmp(buf + pos, "_type", 5)) {
+ return buf;
+ }
+ return flatcc_json_parser_match_symbol(ctx, buf, end, pos + 5);
+}
+
+const char *flatcc_json_parser_unmatched_symbol(flatcc_json_parser_t *ctx, const char *buf, const char *end);
+
+static inline const char *flatcc_json_parser_coerce_uint64(
+ flatcc_json_parser_t *ctx, const char *buf,
+ const char *end, int value_sign, uint64_t value, uint64_t *v)
+{
+ if (value_sign) {
+ return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_underflow);
+ }
+ *v = value;
+ return buf;
+}
+
+static inline const char *flatcc_json_parser_coerce_bool(flatcc_json_parser_t *ctx, const char *buf,
+ const char *end, int value_sign, uint64_t value, uint8_t *v)
+{
+ if (value_sign) {
+ return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_underflow);
+ }
+ *v = (uint8_t)!!value;
+ return buf;
+}
+
+#define __flatcc_json_parser_define_coerce_unsigned(type, basetype, uctype) \
+static inline const char *flatcc_json_parser_coerce_ ## type( \
+ flatcc_json_parser_t *ctx, const char *buf, \
+ const char *end, int value_sign, uint64_t value, basetype *v) \
+{ \
+ if (value_sign) { \
+ return flatcc_json_parser_set_error(ctx, buf, end, \
+ flatcc_json_parser_error_underflow); \
+ } \
+ if (value > uctype ## _MAX) { \
+ return flatcc_json_parser_set_error(ctx, buf, end, \
+ flatcc_json_parser_error_overflow); \
+ } \
+ *v = (basetype)value; \
+ return buf; \
+}
+
+__flatcc_json_parser_define_coerce_unsigned(uint32, uint32_t, UINT32)
+__flatcc_json_parser_define_coerce_unsigned(uint16, uint16_t, UINT16)
+__flatcc_json_parser_define_coerce_unsigned(uint8, uint8_t, UINT8)
+
+#define __flatcc_json_parser_define_coerce_signed(type, basetype, uctype) \
+static inline const char *flatcc_json_parser_coerce_ ## type( \
+ flatcc_json_parser_t *ctx, const char *buf, \
+ const char *end, int value_sign, uint64_t value, basetype *v) \
+{ \
+ if (value_sign) { \
+ if (value > (uint64_t)(uctype ## _MAX) + 1) { \
+ return flatcc_json_parser_set_error(ctx, buf, end, \
+ flatcc_json_parser_error_underflow); \
+ } \
+ *v = (basetype)-(int64_t)value; \
+ } else { \
+ if (value > uctype ## _MAX) { \
+ return flatcc_json_parser_set_error(ctx, buf, end, \
+ flatcc_json_parser_error_overflow); \
+ } \
+ *v = (basetype)value; \
+ } \
+ return buf; \
+}
+
+__flatcc_json_parser_define_coerce_signed(int64, int64_t, INT64)
+__flatcc_json_parser_define_coerce_signed(int32, int32_t, INT32)
+__flatcc_json_parser_define_coerce_signed(int16, int16_t, INT16)
+__flatcc_json_parser_define_coerce_signed(int8, int8_t, INT8)
+
+static inline const char *flatcc_json_parser_coerce_float(
+ flatcc_json_parser_t *ctx, const char *buf,
+ const char *end, int value_sign, uint64_t value, float *v)
+{
+ (void)ctx;
+ (void)end;
+
+ *v = value_sign ? -(float)value : (float)value;
+ return buf;
+}
+
+static inline const char *flatcc_json_parser_coerce_double(
+ flatcc_json_parser_t *ctx, const char *buf,
+ const char *end, int value_sign, uint64_t value, double *v)
+{
+ (void)ctx;
+ (void)end;
+
+ *v = value_sign ? -(double)value : (double)value;
+ return buf;
+}
+
+const char *flatcc_json_parser_double(flatcc_json_parser_t *ctx, const char *buf, const char *end, double *v);
+
+const char *flatcc_json_parser_float(flatcc_json_parser_t *ctx, const char *buf, const char *end, float *v);
+
+/*
+ * If the buffer does not contain a valid start character for a numeric
+ * value, the function will return the the input buffer without failure.
+ * This makes is possible to try a symbolic parse.
+ */
+const char *flatcc_json_parser_integer(flatcc_json_parser_t *ctx, const char *buf, const char *end,
+ int *value_sign, uint64_t *value);
+
+/* Returns unchanged buffer without error if `null` is not matched. */
+static inline const char *flatcc_json_parser_null(const char *buf, const char *end)
+{
+ if (end - buf >= 4 && memcmp(buf, "null", 4) == 0) {
+ return buf + 4;
+ }
+ return buf;
+}
+
+/*
+ * `parsers` is a null terminated array of parsers with at least one
+ * valid parser. A numeric literal parser may also be included.
+ */
+#define __flatcc_json_parser_define_integral_parser(type, basetype) \
+static inline const char *flatcc_json_parser_ ## type( \
+ flatcc_json_parser_t *ctx, \
+ const char *buf, const char *end, basetype *v) \
+{ \
+ uint64_t value = 0; \
+ int value_sign = 0; \
+ const char *mark = buf; \
+ \
+ *v = 0; \
+ if (buf == end) { \
+ return buf; \
+ } \
+ buf = flatcc_json_parser_integer(ctx, buf, end, &value_sign, &value); \
+ if (buf != mark) { \
+ return flatcc_json_parser_coerce_ ## type(ctx, \
+ buf, end, value_sign, value, v); \
+ } \
+ return buf; \
+}
+
+__flatcc_json_parser_define_integral_parser(uint64, uint64_t)
+__flatcc_json_parser_define_integral_parser(uint32, uint32_t)
+__flatcc_json_parser_define_integral_parser(uint16, uint16_t)
+__flatcc_json_parser_define_integral_parser(uint8, uint8_t)
+__flatcc_json_parser_define_integral_parser(int64, int64_t)
+__flatcc_json_parser_define_integral_parser(int32, int32_t)
+__flatcc_json_parser_define_integral_parser(int16, int16_t)
+__flatcc_json_parser_define_integral_parser(int8, int8_t)
+
+static inline const char *flatcc_json_parser_bool(flatcc_json_parser_t *ctx, const char *buf, const char *end, uint8_t *v)
+{
+ const char *k;
+ uint8_t tmp;
+
+ k = buf;
+ if (end - buf >= 4 && memcmp(buf, "true", 4) == 0) {
+ *v = 1;
+ return k + 4;
+ } else if (end - buf >= 5 && memcmp(buf, "false", 5) == 0) {
+ *v = 0;
+ return k + 5;
+ }
+ buf = flatcc_json_parser_uint8(ctx, buf, end, &tmp);
+ *v = !!tmp;
+ return buf;
+}
+
+/*
+ * The `parsers` argument is a zero terminated array of parser
+ * functions with increasingly general scopes.
+ *
+ * Symbols can be be or'ed together by listing multiple space separated
+ * flags in source being parsed, like `{ x : "Red Blue" }`.
+ * Intended for flags, but generally available.
+ *
+ * `aggregate` means there are more symbols to follow.
+ *
+ * This function does not return input `buf` value if match was
+ * unsuccessful. It will either match or error.
+ */
+typedef const char *flatcc_json_parser_integral_symbol_f(flatcc_json_parser_t *ctx,
+ const char *buf, const char *end, int *value_sign, uint64_t *value, int *aggregate);
+
+/*
+ * Raise an error if a syntax like `color: Red Green` is seen unless
+ * explicitly permitted. `color: "Red Green"` or `"color": "Red Green"
+ * or `color: Red` is permitted if unquoted is permitted but not
+ * unquoted list. Googles flatc JSON parser does not allow multiple
+ * symbolic values unless quoted, so this is the default.
+ */
+#if !FLATCC_JSON_PARSE_ALLOW_UNQUOTED || FLATCC_JSON_PARSE_ALLOW_UNQUOTED_LIST
+#define __flatcc_json_parser_init_check_unquoted_list()
+#define __flatcc_json_parser_check_unquoted_list()
+#else
+#define __flatcc_json_parser_init_check_unquoted_list() int list_count = 0;
+#define __flatcc_json_parser_check_unquoted_list() \
+ if (list_count++ && ctx->unquoted) { \
+ return flatcc_json_parser_set_error(ctx, buf, end, \
+ flatcc_json_parser_error_unquoted_symbolic_list); \
+ }
+#endif
+
+#define __flatcc_json_parser_define_symbolic_integral_parser(type, basetype)\
+static const char *flatcc_json_parser_symbolic_ ## type( \
+ flatcc_json_parser_t *ctx, \
+ const char *buf, const char *end, \
+ flatcc_json_parser_integral_symbol_f *parsers[], \
+ basetype *v) \
+{ \
+ flatcc_json_parser_integral_symbol_f **p; \
+ const char *mark; \
+ basetype tmp = 0; \
+ uint64_t value; \
+ int value_sign, aggregate; \
+ __flatcc_json_parser_init_check_unquoted_list() \
+ \
+ *v = 0; \
+ buf = flatcc_json_parser_constant_start(ctx, buf, end); \
+ if (buf == end) { \
+ return buf; \
+ } \
+ do { \
+ p = parsers; \
+ do { \
+ /* call parser function */ \
+ buf = (*p)(ctx, (mark = buf), end, \
+ &value_sign, &value, &aggregate); \
+ if (buf == end) { \
+ return buf; \
+ } \
+ } while (buf == mark && *++p); \
+ if (mark == buf) { \
+ return flatcc_json_parser_set_error(ctx, buf, end, \
+ flatcc_json_parser_error_expected_scalar); \
+ } \
+ __flatcc_json_parser_check_unquoted_list() \
+ if (end == flatcc_json_parser_coerce_ ## type(ctx, \
+ buf, end, value_sign, value, &tmp)) { \
+ return end; \
+ } \
+ /* \
+ * `+=`, not `|=` because we also coerce to float and double, \
+ * and because we need to handle signed values. This may give \
+ * unexpected results with duplicate flags. \
+ */ \
+ *v += tmp; \
+ } while (aggregate); \
+ return buf; \
+}
+
+__flatcc_json_parser_define_symbolic_integral_parser(uint64, uint64_t)
+__flatcc_json_parser_define_symbolic_integral_parser(uint32, uint32_t)
+__flatcc_json_parser_define_symbolic_integral_parser(uint16, uint16_t)
+__flatcc_json_parser_define_symbolic_integral_parser(uint8, uint8_t)
+__flatcc_json_parser_define_symbolic_integral_parser(int64, int64_t)
+__flatcc_json_parser_define_symbolic_integral_parser(int32, int32_t)
+__flatcc_json_parser_define_symbolic_integral_parser(int16, int16_t)
+__flatcc_json_parser_define_symbolic_integral_parser(int8, int8_t)
+
+__flatcc_json_parser_define_symbolic_integral_parser(bool, uint8_t)
+
+/* We still parse integral values, but coerce to float or double. */
+__flatcc_json_parser_define_symbolic_integral_parser(float, float)
+__flatcc_json_parser_define_symbolic_integral_parser(double, double)
+
+/*
+ * This doesn't do anything other than validate and advance past
+ * a JSON value which may use unquoted symbols.
+ *
+ * Upon call it is assumed that leading space has been stripped and that
+ * a JSON value is expected (i.e. root, or just after ':' in a
+ * container object, or less likely as an array member). Any trailing
+ * comma is assumed to belong to the parent context. Returns a parse
+ * location stripped from space so container should post call expect
+ * ',', '}', or ']', or EOF if the JSON is valid.
+ */
+const char *flatcc_json_parser_generic_json(flatcc_json_parser_t *ctx, const char *buf, const char *end);
+
+typedef const char *flatcc_json_parser_union_f(flatcc_json_parser_t *ctx,
+ const char *buf, const char *end, uint8_t type, flatbuffers_voffset_t id);
+
+/* Called at start by table parsers with at least 1 union. */
+const char *flatcc_json_parser_prepare_unions(flatcc_json_parser_t *ctx,
+ const char *buf, const char *end, size_t union_total);
+
+const char *flatcc_json_parser_finalize_unions(flatcc_json_parser_t *ctx,
+ const char *buf, const char *end);
+
+const char *flatcc_json_parser_union(flatcc_json_parser_t *ctx,
+ const char *buf, const char *end, size_t union_index,
+ flatbuffers_voffset_t id, flatcc_json_parser_union_f *parse);
+
+const char *flatcc_json_parser_union_type(flatcc_json_parser_t *ctx,
+ const char *buf, const char *end, size_t union_index, flatbuffers_voffset_t id,
+ flatcc_json_parser_integral_symbol_f *type_parsers[],
+ flatcc_json_parser_union_f *union_parser);
+
+#include "flatcc/portable/pdiagnostic_pop.h"
+#endif /* FLATCC_JSON_PARSE_H */
diff --git a/Source/library/FBSUtil/flatcc/flatcc_json_printer.h b/Source/library/FBSUtil/flatcc/flatcc_json_printer.h
new file mode 100644
index 0000000..4b12f10
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/flatcc_json_printer.h
@@ -0,0 +1,651 @@
+#ifndef FLATCC_JSON_PRINTER_H
+#define FLATCC_JSON_PRINTER_H
+
+/*
+ * Definitions for default implementation, do not assume these are
+ * always valid.
+ */
+#define FLATCC_JSON_PRINT_FLUSH_SIZE (1024 * 16)
+#define FLATCC_JSON_PRINT_RESERVE 64
+#define FLATCC_JSON_PRINT_BUFFER_SIZE (FLATCC_JSON_PRINT_FLUSH_SIZE + FLATCC_JSON_PRINT_RESERVE)
+
+/* Initial size that grows exponentially. */
+#define FLATCC_JSON_PRINT_DYN_BUFFER_SIZE 4096
+
+
+#include
+#include
+
+#include "flatcc/flatcc_rtconfig.h"
+#include "flatcc/flatcc_flatbuffers.h"
+
+/* -DFLATCC_PORTABLE may help if inttypes.h is missing. */
+#ifndef PRId64
+#include
+#endif
+
+#define FLATCC_JSON_PRINT_ERROR_MAP(XX) \
+ XX(ok, "ok") \
+ /* \
+ * When the flatbuffer is null, has too small a header, or has \
+ * mismatching identifier when a match was requested. \
+ */ \
+ XX(bad_input, "bad input") \
+ XX(deep_recursion, "deep recursion") \
+ /* \
+ * When the output was larger than the available fixed size buffer, \
+ * or dynamic allocation could not grow the buffer sufficiently. \
+ */ \
+ XX(overflow, "overflow")
+
+enum flatcc_json_printer_error_no {
+#define XX(no, str) flatcc_json_printer_error_##no,
+ FLATCC_JSON_PRINT_ERROR_MAP(XX)
+#undef XX
+};
+
+#define flatcc_json_printer_ok flatcc_json_printer_error_ok
+
+typedef struct flatcc_json_printer_ctx flatcc_json_printer_t;
+
+typedef void flatcc_json_printer_flush_f(flatcc_json_printer_t *ctx, int all);
+
+struct flatcc_json_printer_ctx {
+ char *buf;
+ size_t size;
+ size_t flush_size;
+ size_t total;
+ const char *pflush;
+ char *p;
+ uint8_t own_buffer;
+ uint8_t indent;
+ uint8_t unquote;
+ uint8_t noenum;
+ uint8_t skip_default;
+ uint8_t force_default;
+ int level;
+ int error;
+
+ void *fp;
+ flatcc_json_printer_flush_f *flush;
+};
+
+static inline void flatcc_json_printer_set_error(flatcc_json_printer_t *ctx, int err)
+{
+ if (!ctx->error) {
+ ctx->error = err;
+ }
+}
+
+const char *flatcc_json_printer_error_string(int err);
+
+static inline int flatcc_json_printer_get_error(flatcc_json_printer_t *ctx)
+{
+ return ctx->error;
+}
+
+/*
+ * Call to reuse context between parses without without
+ * returning buffer. If a file pointer is being used,
+ * it will remain open.
+ *
+ * Reset does not affect the formatting settings indentation, and
+ * operational flags, but does zero the indentation level.
+ */
+static inline void flatcc_json_printer_reset(flatcc_json_printer_t *ctx)
+{
+ ctx->p = ctx->buf;
+ ctx->level = 0;
+ ctx->total = 0;
+ ctx->error = 0;
+}
+
+/*
+ * A custom init function can be implemented with a custom flush
+ * function can be custom implemented. A few have been provided:
+ * init with external fixed size buffer, and init with dynamically
+ * growing buffer.
+ *
+ * Because there are a lot of small print functions, it is essentially
+ * always faster to print to local buffer than moving to io directly
+ * such as using fprintf or fwrite. The flush callback is used to
+ * move data when enough has been collected.
+ *
+ * `fp` should be of type `FILE *` but we do not enforce it here
+ * because it allows the header to be independent of
+ * when not required. If `fp` is null, it defaults to stdout.
+ *
+ * Returns -1 on alloc error (no cleanup needed), or 0 on success.
+ * Eventually the clear method must be called to return memory.
+ *
+ * The file pointer may be stdout or a custom file. The file pointer
+ * is not affected by reset or clear and should be closed manually.
+ *
+ * `set_flags` and related may be called subsequently to modify
+ * behavior.
+ */
+int flatcc_json_printer_init(flatcc_json_printer_t *ctx, void *fp);
+
+/*
+ * Prints to external buffer and sets overflow error if buffer is too
+ * small. Earlier content is then overwritten. A custom version of this
+ * function could flush the content to elsewhere before allowing the
+ * buffer content to be overwritten. The `buffers_size` must be large
+ * enough to hold `FLATCC_JSON_PRINT_RESERVED_SIZE` which is small but
+ * large enough value to hold entire numbers and the like.
+ *
+ * It is not strictly necessary to call clear because the buffer is
+ * external, but still good form and case the context type is changed
+ * later.
+ *
+ * Returns -1 on buffer size error (no cleanup needed), or 0 on success.
+ *
+ * `set_flags` and related may be called subsequently to modify
+ * behavior.
+ */
+int flatcc_json_printer_init_buffer(flatcc_json_printer_t *ctx, char *buffer, size_t buffer_size);
+
+/*
+ * Returns the current buffer pointer and also the content size in
+ * `buffer_size` if it is null. The operation is not very useful for
+ * file oriented printers (created with `init`) and will then only
+ * return the unflushed buffer content. For fixed size buffers
+ * (`init_buffer`), only the last content is available if the buffer
+ * overflowed. Works well with (`init_buffer`) when the dynamic buffer
+ * is be reused, otherwise `finalize_dynamic_buffer` could be more
+ * appropriate.
+ *
+ * The returned buffer is zero terminated.
+ *
+ * The returned pointer is only valid until next operation and should
+ * not deallocated manually.
+ */
+void *flatcc_json_printer_get_buffer(flatcc_json_printer_t *ctx, size_t *buffer_size);
+
+/*
+ * Set to non-zero if names and enum symbols can be unquoted thus
+ * diverging from standard JSON while remaining compatible with `flatc`
+ * JSON flavor.
+ */
+static inline void flatcc_json_printer_set_unquoted(flatcc_json_printer_t *ctx, int x)
+{
+ ctx->unquote = !!x;
+}
+
+/*
+ * Set to non-zero if enums should always be printed as numbers.
+ * Otherwise enums are printed as a symbol for member values, and as
+ * numbers for other values.
+ *
+ * NOTE: this setting will not affect code generated with enum mapping
+ * disabled - statically disabling enum mapping is signficantly faster
+ * for enums, less so for for union types.
+ */
+static inline void flatcc_json_printer_set_noenum(flatcc_json_printer_t *ctx, int x)
+{
+ ctx->noenum = !!x;
+}
+
+/*
+ * Override priting an existing scalar field if it equals the default value.
+ * Note that this setting is not mutually exclusive to `set_force_default`.
+ */
+static inline void flatcc_json_printer_set_skip_default(flatcc_json_printer_t *ctx, int x)
+{
+ ctx->skip_default = !!x;
+}
+
+/*
+ * Override skipping absent scalar fields and print the default value.
+ * Note that this setting is not mutually exclusive to `set_skip_default`.
+ */
+static inline void flatcc_json_printer_set_force_default(flatcc_json_printer_t *ctx, int x)
+{
+ ctx->force_default = !!x;
+}
+
+
+/*
+ * Set pretty-print indentation in number of spaces. 0 (default) is
+ * compact with no spaces or linebreaks (default), anything above
+ * triggers pretty print.
+ */
+static inline void flatcc_json_printer_set_indent(flatcc_json_printer_t *ctx, uint8_t x)
+{
+ ctx->indent = x;
+}
+
+/*
+ * Override the default compact valid JSON format with a
+ * pretty printed non-strict version. Enums are translated
+ * to names, which is also the default.
+ */
+static inline void flatcc_json_printer_set_nonstrict(flatcc_json_printer_t *ctx)
+{
+ flatcc_json_printer_set_indent(ctx, 2);
+ flatcc_json_printer_set_unquoted(ctx, 1);
+ flatcc_json_printer_set_noenum(ctx, 0);
+}
+
+enum flatcc_json_printer_flags {
+ flatcc_json_printer_f_unquote = 1,
+ flatcc_json_printer_f_noenum = 2,
+ flatcc_json_printer_f_skip_default = 4,
+ flatcc_json_printer_f_force_default = 8,
+ flatcc_json_printer_f_pretty = 16,
+ flatcc_json_printer_f_nonstrict = 32,
+};
+
+/*
+ * May be called instead of setting operational modes individually.
+ * Formatting is strict quoted json witout pretty printing by default.
+ *
+ * flags are:
+ *
+ * `unquote`,
+ * `noenum`,
+ * `skip_default`,
+ * `force_default`,
+ * `pretty`,
+ * `nonstrict`
+ *
+ * `pretty` flag sets indentation to 2.
+ * `nonstrict` implies: `noenum`, `unquote`, `pretty`.
+ */
+static inline void flatcc_json_printer_set_flags(flatcc_json_printer_t *ctx, int flags)
+{
+ ctx->unquote = !!(flags & flatcc_json_printer_f_unquote);
+ ctx->noenum = !!(flags & flatcc_json_printer_f_noenum);
+ ctx->skip_default = !!(flags & flatcc_json_printer_f_skip_default);
+ ctx->force_default = !!(flags & flatcc_json_printer_f_force_default);
+ if (flags & flatcc_json_printer_f_pretty) {
+ flatcc_json_printer_set_indent(ctx, 2);
+ }
+ if (flags & flatcc_json_printer_f_nonstrict) {
+ flatcc_json_printer_set_nonstrict(ctx);
+ }
+}
+
+
+/*
+ * Detects if the conctext type uses dynamically allocated memory
+ * using malloc and realloc and frees any such memory.
+ *
+ * Not all context types needs to be cleared.
+ */
+void flatcc_json_printer_clear(flatcc_json_printer_t *ctx);
+
+/*
+ * Ensures that there ia always buffer capacity for priting the next
+ * primitive with delimiters.
+ *
+ * Only flushes complete flush units and is inexpensive to call.
+ * The content buffer has an extra reserve which ensures basic
+ * data types and delimiters can always be printed after a partial
+ * flush. At the end, a `flush` is required to flush the
+ * remaining incomplete buffer data.
+ *
+ * Numbers do not call partial flush but will always fit into the reserve
+ * capacity after a partial flush, also surrounded by delimiters.
+ *
+ * Variable length operations generally submit a partial flush so it is
+ * safe to print a number after a name without flushing, but vectors of
+ * numbers must (and do) issue a partial flush between elements. This is
+ * handled automatically but must be considered if using the primitives
+ * for special purposes. Because repeated partial flushes are very cheap
+ * this is only a concern for high performance applications.
+ *
+ * When identiation is enabled, partial flush is also automatically
+ * issued .
+ */
+static inline void flatcc_json_printer_flush_partial(flatcc_json_printer_t *ctx)
+{
+ if (ctx->p >= ctx->pflush) {
+ ctx->flush(ctx, 0);
+ }
+}
+
+/*
+ * Flush the remaining data not flushed by partial flush. It is valid to
+ * call at any point if it is acceptable to have unaligned flush units,
+ * but this is not desireable if, for example, compression or encryption
+ * is added to the flush pipeline.
+ *
+ * Not called automatically at the end of printing a flatbuffer object
+ * in case more data needs to be appended without submitting incomplete
+ * flush units prematurely - for example adding a newline at the end.
+ *
+ * The flush behavior depeends on the underlying `ctx` object, for
+ * example dynamic buffers have no distinction between partial and full
+ * flushes - here it is merely ensured that the buffer always has a
+ * reserve capacity left.
+ *
+ * Returns the total printed size.
+ */
+static inline size_t flatcc_json_printer_flush(flatcc_json_printer_t *ctx)
+{
+ ctx->flush(ctx, 1);
+ return ctx->total;
+}
+
+/*
+ * Helper functions to print anything into the json buffer.
+ * Strings are escaped.
+ *
+ * When pretty printing (indent > 0), level 0 has special significance -
+ * so if wrapping printed json in a manually printed container json
+ * object, these functions can help manage this.
+ */
+
+/* Escaped and quoted string. */
+void flatcc_json_printer_string(flatcc_json_printer_t *ctx, const char *s, int n);
+/* Unescaped and unquoted string. */
+void flatcc_json_printer_write(flatcc_json_printer_t *ctx, const char *s, int n);
+/* Print a newline and issues a partial flush. */
+void flatcc_json_printer_nl(flatcc_json_printer_t *ctx);
+/* Like numbers, a partial flush is not issued. */
+void flatcc_json_printer_char(flatcc_json_printer_t *ctx, char c);
+/* Indents and issues a partial flush. */
+void flatcc_json_printer_indent(flatcc_json_printer_t *ctx);
+/* Adjust identation level, usually +/-1. */
+void flatcc_json_printer_add_level(flatcc_json_printer_t *ctx, int n);
+/* Returns current identation level (0 is top level). */
+int flatcc_json_printer_get_level(flatcc_json_printer_t *ctx);
+
+/*
+ * If called explicitly be aware that repeated calls to numeric
+ * printers may cause buffer overflow without flush in-between.
+ */
+void flatcc_json_printer_uint8(flatcc_json_printer_t *ctx, uint8_t v);
+void flatcc_json_printer_uint16(flatcc_json_printer_t *ctx, uint16_t v);
+void flatcc_json_printer_uint32(flatcc_json_printer_t *ctx, uint32_t v);
+void flatcc_json_printer_uint64(flatcc_json_printer_t *ctx, uint64_t v);
+void flatcc_json_printer_int8(flatcc_json_printer_t *ctx, int8_t v);
+void flatcc_json_printer_int16(flatcc_json_printer_t *ctx, int16_t v);
+void flatcc_json_printer_int32(flatcc_json_printer_t *ctx, int32_t v);
+void flatcc_json_printer_int64(flatcc_json_printer_t *ctx, int64_t v);
+void flatcc_json_printer_bool(flatcc_json_printer_t *ctx, int v);
+void flatcc_json_printer_float(flatcc_json_printer_t *ctx, float v);
+void flatcc_json_printer_double(flatcc_json_printer_t *ctx, double v);
+
+void flatcc_json_printer_enum(flatcc_json_printer_t *ctx,
+ const char *symbol, int len);
+
+/*
+ * Convenience function to add a trailing newline, flush the buffer,
+ * test for error and reset the context for reuse.
+ *
+ * Returns total size printed or < 0 on error.
+ *
+ * This function makes most sense for file oriented output.
+ * See also `finalize_dynamic_buffer`.
+ */
+static inline int flatcc_json_printer_finalize(flatcc_json_printer_t *ctx)
+{
+ int ret;
+ flatcc_json_printer_nl(ctx);
+ ret = (int)flatcc_json_printer_flush(ctx);
+ if (ctx->error) {
+ ret = -1;
+ }
+ flatcc_json_printer_reset(ctx);
+ return ret;
+}
+
+/*
+ * Allocates a small buffer and grows it dynamically.
+ * Buffer survives past reset. To reduce size between uses, call clear
+ * followed by init call. To reuse buffer just call reset between uses.
+ * If `buffer_size` is 0 a sensible default is being used. The size is
+ * automatically rounded up to reserved size if too small.
+ *
+ * Returns -1 on alloc error (no cleanup needed), or 0 on success.
+ * Eventually the clear method must be called to return memory.
+ *
+ * `set_flags` and related may be called subsequently to modify
+ * behavior.
+ */
+int flatcc_json_printer_init_dynamic_buffer(flatcc_json_printer_t *ctx, size_t buffer_size);
+
+/*
+ * Similar to calling `finalize` but returns the buffer and does NOT
+ * reset, but rather clears printer object and the returned buffer must
+ * be deallocated with `free`.
+ *
+ * The returned buffer is zero terminated.
+ *
+ * NOTE: it is entirely optional to use this method. For repeated used
+ * of dynamic buffers, `newline` (or not) followed by `get_buffer`
+ * and `reset` will be an alternative.
+ *
+ * Stores the printed buffer size in `buffer_size` if it is not null.
+ *
+ * See also `get_dynamic_buffer`.
+ */
+void *flatcc_json_printer_finalize_dynamic_buffer(flatcc_json_printer_t *ctx, size_t *buffer_size);
+
+
+/*************************************************************
+ * The following is normally only used by generated code.
+ *************************************************************/
+
+typedef struct flatcc_json_printer_table_descriptor flatcc_json_printer_table_descriptor_t;
+
+struct flatcc_json_printer_table_descriptor {
+ const void *table;
+ const void *vtable;
+ int vsize;
+ int ttl;
+ int count;
+};
+
+typedef void flatcc_json_printer_table_f(flatcc_json_printer_t *ctx,
+ flatcc_json_printer_table_descriptor_t *td);
+
+typedef void flatcc_json_printer_struct_f(flatcc_json_printer_t *ctx,
+ const void *p);
+
+/* Generated value to name map callbacks. */
+typedef void flatcc_json_printer_uint8_enum_f(flatcc_json_printer_t *ctx, uint8_t v);
+typedef void flatcc_json_printer_uint16_enum_f(flatcc_json_printer_t *ctx, uint16_t v);
+typedef void flatcc_json_printer_uint32_enum_f(flatcc_json_printer_t *ctx, uint32_t v);
+typedef void flatcc_json_printer_uint64_enum_f(flatcc_json_printer_t *ctx, uint64_t v);
+typedef void flatcc_json_printer_int8_enum_f(flatcc_json_printer_t *ctx, int8_t v);
+typedef void flatcc_json_printer_int16_enum_f(flatcc_json_printer_t *ctx, int16_t v);
+typedef void flatcc_json_printer_int32_enum_f(flatcc_json_printer_t *ctx, int32_t v);
+typedef void flatcc_json_printer_int64_enum_f(flatcc_json_printer_t *ctx, int64_t v);
+typedef void flatcc_json_printer_bool_enum_f(flatcc_json_printer_t *ctx, flatbuffers_bool_t v);
+
+#define __define_print_scalar_field_proto(TN, T) \
+void flatcc_json_printer_ ## TN ## _field(flatcc_json_printer_t *ctx, \
+ flatcc_json_printer_table_descriptor_t *td, \
+ int id, const char *name, int len, T v);
+
+#define __define_print_scalar_struct_field_proto(TN, T) \
+void flatcc_json_printer_ ## TN ## _struct_field(flatcc_json_printer_t *ctx,\
+ int index, const void *p, size_t offset, \
+ const char *name, int len);
+
+#define __define_print_enum_struct_field_proto(TN, T) \
+void flatcc_json_printer_ ## TN ## _enum_struct_field( \
+ flatcc_json_printer_t *ctx, \
+ int index, const void *p, size_t offset, \
+ const char *name, int len, \
+ flatcc_json_printer_ ## TN ##_enum_f *pf);
+
+#define __define_print_enum_field_proto(TN, T) \
+void flatcc_json_printer_ ## TN ## _enum_field(flatcc_json_printer_t *ctx, \
+ flatcc_json_printer_table_descriptor_t *td, \
+ int id, const char *name, int len, T v, \
+ flatcc_json_printer_ ## TN ##_enum_f *pf);
+
+#define __define_print_scalar_vector_field_proto(TN, T) \
+void flatcc_json_printer_ ## TN ## _vector_field(flatcc_json_printer_t *ctx,\
+ flatcc_json_printer_table_descriptor_t *td, \
+ int id, const char *name, int len);
+
+#define __define_print_enum_vector_field_proto(TN, T) \
+void flatcc_json_printer_ ## TN ## _enum_vector_field( \
+ flatcc_json_printer_t *ctx, \
+ flatcc_json_printer_table_descriptor_t *td, \
+ int id, const char *name, int len, \
+ flatcc_json_printer_ ## TN ##_enum_f *pf);
+
+__define_print_scalar_field_proto(uint8, uint8_t)
+__define_print_scalar_field_proto(uint16, uint16_t)
+__define_print_scalar_field_proto(uint32, uint32_t)
+__define_print_scalar_field_proto(uint64, uint64_t)
+__define_print_scalar_field_proto(int8, int8_t)
+__define_print_scalar_field_proto(int16, int16_t)
+__define_print_scalar_field_proto(int32, int32_t)
+__define_print_scalar_field_proto(int64, int64_t)
+__define_print_scalar_field_proto(bool, flatbuffers_bool_t)
+__define_print_scalar_field_proto(float, float)
+__define_print_scalar_field_proto(double, double)
+
+__define_print_enum_field_proto(uint8, uint8_t)
+__define_print_enum_field_proto(uint16, uint16_t)
+__define_print_enum_field_proto(uint32, uint32_t)
+__define_print_enum_field_proto(uint64, uint64_t)
+__define_print_enum_field_proto(int8, int8_t)
+__define_print_enum_field_proto(int16, int16_t)
+__define_print_enum_field_proto(int32, int32_t)
+__define_print_enum_field_proto(int64, int64_t)
+__define_print_enum_field_proto(bool, flatbuffers_bool_t)
+
+__define_print_scalar_struct_field_proto(uint8, uint8_t)
+__define_print_scalar_struct_field_proto(uint16, uint16_t)
+__define_print_scalar_struct_field_proto(uint32, uint32_t)
+__define_print_scalar_struct_field_proto(uint64, uint64_t)
+__define_print_scalar_struct_field_proto(int8, int8_t)
+__define_print_scalar_struct_field_proto(int16, int16_t)
+__define_print_scalar_struct_field_proto(int32, int32_t)
+__define_print_scalar_struct_field_proto(int64, int64_t)
+__define_print_scalar_struct_field_proto(bool, flatbuffers_bool_t)
+__define_print_scalar_struct_field_proto(float, float)
+__define_print_scalar_struct_field_proto(double, double)
+
+__define_print_enum_struct_field_proto(uint8, uint8_t)
+__define_print_enum_struct_field_proto(uint16, uint16_t)
+__define_print_enum_struct_field_proto(uint32, uint32_t)
+__define_print_enum_struct_field_proto(uint64, uint64_t)
+__define_print_enum_struct_field_proto(int8, int8_t)
+__define_print_enum_struct_field_proto(int16, int16_t)
+__define_print_enum_struct_field_proto(int32, int32_t)
+__define_print_enum_struct_field_proto(int64, int64_t)
+__define_print_enum_struct_field_proto(bool, flatbuffers_bool_t)
+
+__define_print_scalar_vector_field_proto(uint8, uint8_t)
+__define_print_scalar_vector_field_proto(uint16, uint16_t)
+__define_print_scalar_vector_field_proto(uint32, uint32_t)
+__define_print_scalar_vector_field_proto(uint64, uint64_t)
+__define_print_scalar_vector_field_proto(int8, int8_t)
+__define_print_scalar_vector_field_proto(int16, int16_t)
+__define_print_scalar_vector_field_proto(int32, int32_t)
+__define_print_scalar_vector_field_proto(int64, int64_t)
+__define_print_scalar_vector_field_proto(bool, flatbuffers_bool_t)
+__define_print_scalar_vector_field_proto(float, float)
+__define_print_scalar_vector_field_proto(double, double)
+
+__define_print_enum_vector_field_proto(uint8, uint8_t)
+__define_print_enum_vector_field_proto(uint16, uint16_t)
+__define_print_enum_vector_field_proto(uint32, uint32_t)
+__define_print_enum_vector_field_proto(uint64, uint64_t)
+__define_print_enum_vector_field_proto(int8, int8_t)
+__define_print_enum_vector_field_proto(int16, int16_t)
+__define_print_enum_vector_field_proto(int32, int32_t)
+__define_print_enum_vector_field_proto(int64, int64_t)
+__define_print_enum_vector_field_proto(bool, flatbuffers_bool_t)
+
+/*
+ * If `fid` is null, the identifier is not checked and is allowed to be
+ * entirely absent.
+ *
+ * The buffer must at least be aligned to uoffset_t on systems that
+ * require aligned memory addresses (as always for flatbuffers).
+ */
+int flatcc_json_printer_table_as_root(flatcc_json_printer_t *ctx,
+ const void *buf, size_t bufsiz, const char *fid,
+ flatcc_json_printer_table_f *pf);
+
+int flatcc_json_printer_struct_as_root(flatcc_json_printer_t *ctx,
+ const void *buf, size_t bufsiz, const char *fid,
+ flatcc_json_printer_struct_f *pf);
+
+/*
+ * Call before and after enum flags to ensure proper quotation. Enum
+ * quotes may be configured runtime, but regardless of this, multiple
+ * flags may be forced to be quoted depending on compile time flag since
+ * not all parsers may be able to handle unquoted space separated values
+ * even if they handle non-strict unquoted json otherwise.
+ *
+ * Flags should only be called when not empty (0) and when there are no
+ * unknown flags in the value. Otherwise print the numeric value. The
+ * auto generated code deals with this.
+ *
+ * This bit twiddling hack may be useful:
+ *
+ * `multiple = 0 != (v & (v - 1);`
+ */
+void flatcc_json_printer_delimit_enum_flags(flatcc_json_printer_t *ctx, int multiple);
+
+/* The index increments from 0 to handle space. It is not the flag bit position. */
+void flatcc_json_printer_enum_flag(flatcc_json_printer_t *ctx, int index, const char *symbol, int len);
+
+/* A struct inside another struct, as opposed to inside a table or a root. */
+void flatcc_json_printer_embedded_struct_field(flatcc_json_printer_t *ctx,
+ int index, const void *p, size_t offset,
+ const char *name, int len,
+ flatcc_json_printer_struct_f pf);
+
+void flatcc_json_printer_struct_field(flatcc_json_printer_t *ctx,
+ flatcc_json_printer_table_descriptor_t *td,
+ int id, const char *name, int len,
+ flatcc_json_printer_struct_f *pf);
+
+void flatcc_json_printer_string_field(flatcc_json_printer_t *ctx,
+ flatcc_json_printer_table_descriptor_t *td,
+ int id, const char *name, int len);
+
+void flatcc_json_printer_string_vector_field(flatcc_json_printer_t *ctx,
+ flatcc_json_printer_table_descriptor_t *td,
+ int id, const char *name, int len);
+
+void flatcc_json_printer_table_field(flatcc_json_printer_t *ctx,
+ flatcc_json_printer_table_descriptor_t *td,
+ int id, const char *name, int len,
+ flatcc_json_printer_table_f pf);
+
+void flatcc_json_printer_struct_vector_field(flatcc_json_printer_t *ctx,
+ flatcc_json_printer_table_descriptor_t *td,
+ int id, const char *name, int len,
+ size_t size,
+ flatcc_json_printer_struct_f pf);
+
+void flatcc_json_printer_table_vector_field(flatcc_json_printer_t *ctx,
+ flatcc_json_printer_table_descriptor_t *td,
+ int id, const char *name, int len,
+ flatcc_json_printer_table_f pf);
+
+void flatcc_json_printer_struct_as_nested_root(flatcc_json_printer_t *ctx,
+ flatcc_json_printer_table_descriptor_t *td,
+ int id, const char *name, int len,
+ const char *fid,
+ flatcc_json_printer_struct_f *pf);
+
+void flatcc_json_printer_table_as_nested_root(flatcc_json_printer_t *ctx,
+ flatcc_json_printer_table_descriptor_t *td,
+ int id, const char *name, int len,
+ const char *fid,
+ flatcc_json_printer_table_f pf);
+
+int flatcc_json_printer_read_union_type(
+ flatcc_json_printer_table_descriptor_t *td,
+ int id);
+
+void flatcc_json_printer_union_type(flatcc_json_printer_t *ctx,
+ flatcc_json_printer_table_descriptor_t *td,
+ const char *name, int len, int type,
+ const char *type_name, int type_len);
+
+#endif /* FLATCC_JSON_PRINTER_H */
diff --git a/Source/library/FBSUtil/flatcc/flatcc_portable.h b/Source/library/FBSUtil/flatcc/flatcc_portable.h
new file mode 100644
index 0000000..f0a7a14
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/flatcc_portable.h
@@ -0,0 +1,4 @@
+#ifndef FLATCC_PORTABLE_H
+#define FLATCC_PORTABLE_H
+#include "flatcc/portable/portable.h"
+#endif /* FLATCC_PORTABLE_H */
diff --git a/Source/library/FBSUtil/flatcc/flatcc_rtconfig.h b/Source/library/FBSUtil/flatcc/flatcc_rtconfig.h
new file mode 100644
index 0000000..2cb2da2
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/flatcc_rtconfig.h
@@ -0,0 +1,144 @@
+#ifndef FLATCC_RTCONFIG_H
+#define FLATCC_RTCONFIG_H
+
+
+/* Include portability layer here since all other files depend on it. */
+#ifdef FLATCC_PORTABLE
+#include "flatcc/portable/portable.h"
+#endif
+
+/*
+ * Fast printing and parsing of double.
+ *
+ * This requires the grisu3/grisu3_* files to be in the include path,
+ * otherwise strod and sprintf will be used (these needed anyway
+ * as fallback for cases not supported by grisu3).
+ */
+#ifndef FLATCC_USE_GRISU3
+#define FLATCC_USE_GRISU3 1
+#endif
+
+/*
+ * This requires compiler that has enabled marc=native or similar so
+ * __SSE4_2__ flag is defined. Otherwise it will have no effect.
+ *
+ * While SSE may be used for different purposes, it has (as of this
+ * writing) only be used to test the effect on JSON whitespace handling
+ * which improved, but not by a lot, assuming 64-bit unligned access is
+ * otherwise available:
+ *
+ * With 8 space indentation, the JSON benchmark handles 308K parse ops/sec
+ * while SSE ups that to 333 parse ops/sec or 336 if \r\n is also
+ * consumed by SSE. Disabling indentation leaves SSE spacing handling
+ * ineffective, and performance reaches 450K parse ops/sec and can
+ * improve further to 500+K parse ops/sec if inexact GRISU3 numbers are
+ * allowed (they are pretty accurate anyway, just not exact). This
+ * feature requires hacking a flag direct in the grisu3 double parsing
+ * lib directly and only mentioned for comparison.
+ *
+ * In conclusion SSE doesn't add a lot to JSON space handling at least.
+ *
+ * Disabled by default, but can be overriden by build system.
+ */
+#ifndef FLATCC_USE_SSE4_2
+#define FLATCC_USE_SSE4_2 0
+#endif
+
+/*
+ * The verifier only reports yes and no. The following setting
+ * enables assertions in debug builds. It must be compiled into
+ * the runtime library and is not normally the desired behavior.
+ *
+ * NOTE: enabling this can break test cases so use with build, not test.
+ */
+#if !defined(FLATCC_DEBUG_VERIFY) && !defined(NDEBUG)
+#define FLATCC_DEBUG_VERIFY 0
+#endif
+
+/*
+ * Limit recursion level for tables. Actual level may be deeper
+ * when structs are deeply nested - but these are limited by the
+ * schema compiler.
+ */
+#ifndef FLATCC_JSON_PRINT_MAX_LEVELS
+#define FLATCC_JSON_PRINT_MAX_LEVELS 100
+#endif
+
+/*
+ * Print float and double values with C99 hexadecimal floating point
+ * notation. This option is not valid JSON but it avoids precision
+ * loss, correctly handles NaN, +/-Infinity and is significantly faster
+ * to parse and print. Some JSON parsers rely on strtod which does
+ * support hexadecimal floating points when C99 compliant.
+ */
+#ifndef FLATCC_JSON_PRINT_HEX_FLOAT
+#define FLATCC_JSON_PRINT_HEX_FLOAT 0
+#endif
+
+/*
+ * Always print multipe enum flags like `color: "Red Green"`
+ * even when unquote is selected as an option for single
+ * value like `color: Green`. Otherwise multiple values
+ * are printed as `color: Red Green`, but this could break
+ * some flatbuffer json parser.
+ */
+#ifndef FLATCC_JSON_PRINT_ALWAYS_QUOTE_MULTIPLE_FLAGS
+#define FLATCC_JSON_PRINT_ALWAYS_QUOTE_MULTIPLE_FLAGS 1
+#endif
+
+/*
+ * The general nesting limit may be lower, but for skipping
+ * JSON we do not need to - we can set this high as it only
+ * costs a single char per level in a stack array.
+ */
+#ifndef FLATCC_JSON_PARSE_GENERIC_MAX_NEST
+#define FLATCC_JSON_PARSE_GENERIC_MAX_NEST 512
+#endif
+
+/* Store value even if it is default. */
+#ifndef FLATCC_JSON_PARSE_FORCE_DEFAULTS
+#define FLATCC_JSON_PARSE_FORCE_DEFAULTS 0
+#endif
+
+#ifndef FLATCC_JSON_PARSE_ALLOW_UNQUOTED
+#define FLATCC_JSON_PARSE_ALLOW_UNQUOTED 1
+#endif
+
+/*
+ * Multiple enum values are by default not permitted unless
+ * quoted like `color: "Red Green" as per Googles flatc JSON
+ * parser while a single value like `color: Red` can be
+ * unquoted. Enabling this setting will allow `color: Red
+ * Green`, but only if FLATCC_JSON_PARSE_ALLOW_UNQUOTED is
+ * also enabled.
+ */
+#ifndef FLATCC_JSON_PARSE_ALLOW_UNQUOTED_LIST
+#define FLATCC_JSON_PARSE_ALLOW_UNQUOTED_LIST 0
+#endif
+
+#ifndef FLATCC_JSON_PARSE_ALLOW_UNKNOWN_FIELD
+#define FLATCC_JSON_PARSE_ALLOW_UNKNOWN_FIELD 1
+#endif
+
+#ifndef FLATCC_JSON_PARSE_ALLOW_TRAILING_COMMA
+#define FLATCC_JSON_PARSE_ALLOW_TRAILING_COMMA 1
+#endif
+
+/*
+ * Just parse to the closing bracket '}' if set.
+ * Otherwise parse to end by consuming space and
+ * fail if anything but space follows.
+ */
+#ifndef FLATCC_PARSE_IGNORE_TRAILING_DATA
+#define FLATCC_PARSE_IGNORE_TRAILING_DATA 0
+#endif
+
+/*
+ * Optimize to parse a lot of white space, but
+ * in most cases it probably slows parsing down.
+ */
+#ifndef FLATCC_JSON_PARSE_WIDE_SPACE
+#define FLATCC_JSON_PARSE_WIDE_SPACE 0
+#endif
+
+#endif /* FLATCC_RTCONFIG_H */
diff --git a/Source/library/FBSUtil/flatcc/flatcc_types.h b/Source/library/FBSUtil/flatcc/flatcc_types.h
new file mode 100644
index 0000000..6182c14
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/flatcc_types.h
@@ -0,0 +1,87 @@
+#ifndef FLATCC_TYPES_H
+#define FLATCC_TYPES_H
+
+#include
+
+#ifndef UINT8_MAX
+#include
+#endif
+
+/*
+ * This should match generated type declaratios in
+ * `flatbuffers_common_reader.h` (might have different name prefix).
+ * Read only generated code does not depend on library code,
+ * hence the duplication.
+ */
+#ifndef flatbuffers_types_defined
+#define flatbuffers_types_defined
+
+/*
+ * uoffset_t and soffset_t must be same integer type, except for sign.
+ * They can be (u)int16_t, (u)int32_t, or (u)int64_t.
+ * The default is (u)int32_t.
+ *
+ * voffset_t is expected to be uint16_t, but can experimentally be
+ * compiled from uint8_t up to uint32_t.
+ *
+ * ID_MAX is the largest value that can index a vtable. The table size
+ * is given as voffset value. Each id represents a voffset value index
+ * from 0 to max inclusive. Space is required for two header voffset
+ * fields and the unaddressible highest index (due to the table size
+ * representation). For 16-bit voffsets this yields a max of 2^15 - 4,
+ * or (2^16 - 1) / 2 - 3.
+ */
+
+#define flatbuffers_uoffset_t_defined
+#define flatbuffers_soffset_t_defined
+#define flatbuffers_voffset_t_defined
+#define flatbuffers_utype_t_defined
+#define flatbuffers_bool_t_defined
+#define flatbuffers_thash_t_defined
+#define flatbuffers_fid_t_defined
+
+/* uoffset_t is also used for vector and string headers. */
+#define FLATBUFFERS_UOFFSET_MAX UINT32_MAX
+#define FLATBUFFERS_SOFFSET_MAX INT32_MAX
+#define FLATBUFFERS_SOFFSET_MIN INT32_MIN
+#define FLATBUFFERS_VOFFSET_MAX UINT16_MAX
+#define FLATBUFFERS_UTYPE_MAX UINT8_MAX
+/* Well - the max of the underlying type. */
+#define FLATBUFFERS_BOOL_MAX UINT8_MAX
+#define FLATBUFFERS_THASH_MAX UINT32_MAX
+
+#define FLATBUFFERS_ID_MAX (FLATBUFFERS_VOFFSET_MAX / sizeof(flatbuffers_voffset_t) - 3)
+/* Vectors of empty structs can yield div by zero, so we must guard against this. */
+#define FLATBUFFERS_COUNT_MAX(elem_size) (FLATBUFFERS_UOFFSET_MAX/((elem_size) == 0 ? 1 : (elem_size)))
+
+#define FLATBUFFERS_UOFFSET_WIDTH 32
+#define FLATBUFFERS_COUNT_WIDTH 32
+#define FLATBUFFERS_SOFFSET_WIDTH 32
+#define FLATBUFFERS_VOFFSET_WIDTH 16
+#define FLATBUFFERS_UTYPE_WIDTH 8
+#define FLATBUFFERS_BOOL_WIDTH 8
+#define FLATBUFFERS_THASH_WIDTH 32
+
+#define FLATBUFFERS_TRUE 1
+#define FLATBUFFERS_FALSE 0
+
+#define FLATBUFFERS_PROTOCOL_IS_LE 1
+#define FLATBUFFERS_PROTOCOL_IS_BE 0
+
+typedef uint32_t flatbuffers_uoffset_t;
+typedef int32_t flatbuffers_soffset_t;
+typedef uint16_t flatbuffers_voffset_t;
+typedef uint8_t flatbuffers_utype_t;
+typedef uint8_t flatbuffers_bool_t;
+typedef uint32_t flatbuffers_thash_t;
+
+static const flatbuffers_bool_t flatbuffers_true = FLATBUFFERS_TRUE;
+static const flatbuffers_bool_t flatbuffers_false = FLATBUFFERS_FALSE;
+
+#define FLATBUFFERS_IDENTIFIER_SIZE (FLATBUFFERS_THASH_WIDTH / 8)
+
+typedef char flatbuffers_fid_t[FLATBUFFERS_IDENTIFIER_SIZE];
+
+#endif /* flatbuffers_types_defined */
+
+#endif /* FLATCC_TYPES_H */
diff --git a/Source/library/FBSUtil/flatcc/flatcc_unaligned.h b/Source/library/FBSUtil/flatcc/flatcc_unaligned.h
new file mode 100644
index 0000000..a53f47a
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/flatcc_unaligned.h
@@ -0,0 +1,8 @@
+#ifndef FLATCC_UNLIGNED_H
+#define FLATCC_UNLIGNED_H
+
+#include "flatcc/portable/punaligned.h"
+
+#define FLATCC_ALLOW_UNALIGNED_ACCESS PORTABLE_UNALIGNED_ACCESS
+
+#endif /* FLATCC_UNLIGNED_H */
diff --git a/Source/library/FBSUtil/flatcc/flatcc_verifier.h b/Source/library/FBSUtil/flatcc/flatcc_verifier.h
new file mode 100644
index 0000000..92483b8
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/flatcc_verifier.h
@@ -0,0 +1,203 @@
+#ifndef FLATCC_VERIFIER_H
+#define FLATCC_VERIFIER_H
+
+/*
+ * Runtime support for verifying flatbuffers.
+ *
+ * Link with the verifier implementation file.
+ *
+ * Note:
+ *
+ * 1) nested buffers will NOT have their identifier verified.
+ * The user may do so subsequently. The reason is in part because
+ * the information is not readily avaible without generated reader code,
+ * in part because the buffer might use a different, but valid,
+ * identifier and the user has no chance of specifiying this in the
+ * verifier code. The root verifier also doesn't assume a specific id
+ * but accepts a user supplied input which may be null.
+ *
+ * 2) All offsets in a buffer are verified for alignment relative to the
+ * buffer start, but the buffer itself is only assumed to aligned to
+ * uoffset_t. A reader should therefore ensure buffer alignment separately
+ * before reading the buffer. Nested buffers are in fact checked for
+ * alignment, but still only relative to the root buffer.
+ *
+ * 3) The max nesting level includes nested buffer nestings, so the
+ * verifier might fail even if the individual buffers are otherwise ok.
+ * This is to prevent abuse with lots of nested buffers.
+ *
+ *
+ * IMPORTANT:
+ *
+ * Even if verifier passes, the buffer may be invalid to access due to
+ * lack of alignemnt in memory, but the verifier is safe to call.
+ *
+ * NOTE: The buffer is not safe to modify after verification because an
+ * attacker may craft overlapping data structures such that modification
+ * of one field updates another in a way that violates the buffer
+ * constraints. This may also be caused by a clever compression scheme.
+ *
+ * It is likely faster to rewrite the table although this is also
+ * dangerous because an attacker (or even normal user) can draft a DAG
+ * that explodes when expanded carelesslessly. A safer approach is to
+ * hash all object references written and reuse those that match. This
+ * will expand references into other objects while bounding expansion
+ * and it will be safe to update assuming shared objects are ok to
+ * update.
+ *
+ */
+
+#include "flatcc/flatcc_types.h"
+
+#define FLATCC_VERIFY_ERROR_MAP(XX)\
+ XX(ok, "ok")\
+ XX(buffer_header_too_small, "buffer header too small")\
+ XX(identifier_mismatch, "identifier mismatch")\
+ XX(max_nesting_level_reached, "max nesting level reached")\
+ XX(required_field_missing, "required field missing")\
+ XX(runtime_buffer_header_not_aligned, "runtime: buffer header not aligned")\
+ XX(runtime_buffer_size_too_large, "runtime: buffer size too large")\
+ XX(string_not_zero_terminated, "string not zero terminated")\
+ XX(string_out_of_range, "string out of range")\
+ XX(struct_out_of_range, "struct out of range")\
+ XX(struct_size_overflow, "struct size overflow")\
+ XX(struct_unaligned, "struct unaligned")\
+ XX(table_field_not_aligned, "table field not aligned")\
+ XX(table_field_out_of_range, "table field out of range")\
+ XX(table_field_size_overflow, "table field size overflow")\
+ XX(table_header_out_of_range_or_unaligned, "table header out of range or unaligned")\
+ XX(table_offset_out_of_range_or_unaligned, "table offset out of range or unaligned")\
+ XX(table_size_out_of_range, "table size out of range")\
+ XX(type_field_absent_from_required_union_field, "type field absent from required union field")\
+ XX(union_cannot_have_a_table_without_a_type, "union cannot have a table without a type")\
+ XX(union_type_NONE_cannot_have_a_table, "union type NONE cannot have a table")\
+ XX(vector_count_exceeds_representable_vector_size, "vector count exceeds representable vector size")\
+ XX(vector_out_of_range, "vector out of range")\
+ XX(vtable_header_out_of_range, "vtable header out of range")\
+ XX(vtable_header_too_small, "vtable header too small")\
+ XX(vtable_offset_out_of_range_or_unaligned, "vtable offset out of range or unaligned")\
+ XX(vtable_size_out_of_range_or_unaligned, "vtable size out of range or unaligned")\
+ XX(vtable_size_overflow, "vtable size overflow")\
+ XX(not_supported, "not supported")
+
+
+enum flatcc_verify_error_no {
+#define XX(no, str) flatcc_verify_error_##no,
+ FLATCC_VERIFY_ERROR_MAP(XX)
+#undef XX
+};
+
+#define flatcc_verify_ok flatcc_verify_error_ok
+
+const char *flatcc_verify_error_string(int err);
+
+
+/*
+ * Type specific table verifier function that checks each known field
+ * for existence in the vtable and then calls the appropriate verifier
+ * function in this library.
+ *
+ * The table descriptor values have been verified for bounds, overflow,
+ * and alignment, but vtable entries after header must be verified
+ * for all fields the table verifier function understands.
+ *
+ * Calls other typespecific verifier functions recursively whenever a
+ * table field, union or table vector is encountered.
+ */
+typedef struct flatcc_table_verifier_descriptor flatcc_table_verifier_descriptor_t;
+struct flatcc_table_verifier_descriptor {
+ /* Pointer to buffer. Not assumed to be aligned beyond uoffset_t. */
+ const void *buf;
+ /* Vtable of current table. */
+ const void *vtable;
+ /* Buffer size. */
+ flatbuffers_uoffset_t end;
+ /* Table offset relative to buffer start */
+ flatbuffers_uoffset_t table;
+ /* Table end relative to buffer start as per vtable[1] field. */
+ flatbuffers_voffset_t tsize;
+ /* Size of vtable in bytes. */
+ flatbuffers_voffset_t vsize;
+ /* Time to live: number nesting levels left before failure. */
+ int ttl;
+};
+
+typedef int flatcc_table_verifier_f(flatcc_table_verifier_descriptor_t *td);
+
+typedef int flatcc_union_verifier_f(flatcc_table_verifier_descriptor_t *td,
+ flatbuffers_voffset_t id, uint8_t type);
+
+
+/*
+ * The `as_root` functions are normally the only functions called
+ * explicitly in this interface.
+ *
+ * If `fid` is null, the identifier is not checked and is allowed to be entirely absent.
+ *
+ * The buffer must at least be aligned to uoffset_t on systems that
+ * require aligned memory addresses. The buffer pointers alignment is
+ * not significant to internal verification of the buffer.
+ */
+int flatcc_verify_struct_as_root(const void *buf, size_t bufsiz, const char *fid,
+ uint16_t align, size_t size);
+
+int flatcc_verify_struct_as_typed_root(const void *buf, size_t bufsiz, flatbuffers_thash_t thash,
+ uint16_t align, size_t size);
+
+int flatcc_verify_table_as_root(const void *buf, size_t bufsiz, const char *fid,
+ flatcc_table_verifier_f *root_tvf);
+
+int flatcc_verify_table_as_typed_root(const void *buf, size_t bufsiz, flatbuffers_thash_t thash,
+ flatcc_table_verifier_f *root_tvf);
+/*
+ * The buffer header is verified by any of the `_as_root` verifiers, but
+ * this function may be used as a quick sanity check.
+ */
+int flatcc_verify_buffer_header(const void *buf, size_t bufsiz, const char *fid);
+
+int flatcc_verify_typed_buffer_header(const void *buf, size_t bufsiz, flatbuffers_thash_t type_hash);
+
+/*
+ * The following functions are typically called by a generated table
+ * verifier function.
+ */
+
+/* Scalar, enum or struct field. */
+int flatcc_verify_field(flatcc_table_verifier_descriptor_t *td,
+ flatbuffers_voffset_t id, uint16_t align, size_t size);
+/* Vector of scalars, enums or structs. */
+int flatcc_verify_vector_field(flatcc_table_verifier_descriptor_t *td,
+ flatbuffers_voffset_t id, int required, uint16_t align, size_t elem_size, size_t max_count);
+int flatcc_verify_string_field(flatcc_table_verifier_descriptor_t *td,
+ flatbuffers_voffset_t id, int required);
+int flatcc_verify_string_vector_field(flatcc_table_verifier_descriptor_t *td,
+ flatbuffers_voffset_t id, int required);
+int flatcc_verify_table_field(flatcc_table_verifier_descriptor_t *td,
+ flatbuffers_voffset_t id, int required, flatcc_table_verifier_f tvf);
+int flatcc_verify_table_vector_field(flatcc_table_verifier_descriptor_t *td,
+ flatbuffers_voffset_t id, int required, flatcc_table_verifier_f tvf);
+
+/* Table verifiers pass 0 as fid. */
+int flatcc_verify_struct_as_nested_root(flatcc_table_verifier_descriptor_t *td,
+ flatbuffers_voffset_t id, int required, const char *fid,
+ uint16_t align, size_t size);
+int flatcc_verify_table_as_nested_root(flatcc_table_verifier_descriptor_t *td,
+ flatbuffers_voffset_t id, int required, const char *fid,
+ uint16_t align, flatcc_table_verifier_f tvf);
+
+/*
+ * A NONE type will not accept a table being present, and a required
+ * union will not accept a type field being absent, and an absent type
+ * field will not accept a table field being present.
+ *
+ * If the above checks out and the type is not NONE, the uvf callback
+ * is executed. It must test each known table type and silently accept
+ * any unknown table type for forward compatibility. A union table
+ * member is verified without the required flag because an absent table
+ * encodes a typed NULL value while an absent type field encodes a
+ * missing union which fails if required.
+ */
+int flatcc_verify_union_field(flatcc_table_verifier_descriptor_t *td,
+ flatbuffers_voffset_t id, int required, flatcc_union_verifier_f *uvf);
+
+#endif /* FLATCC_VERIFIER_H */
diff --git a/Source/library/FBSUtil/flatcc/flatcc_version.h b/Source/library/FBSUtil/flatcc/flatcc_version.h
new file mode 100644
index 0000000..49b1547
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/flatcc_version.h
@@ -0,0 +1,6 @@
+#define FLATCC_VERSION_TEXT "0.4.3-pre"
+#define FLATCC_VERSION_MAJOR 0
+#define FLATCC_VERSION_MINOR 4
+#define FLATCC_VERSION_PATCH 3
+/* 1 or 0 */
+#define FLATCC_VERSION_RELEASED 0
diff --git a/Source/library/FBSUtil/flatcc/portable/LICENSE b/Source/library/FBSUtil/flatcc/portable/LICENSE
new file mode 100644
index 0000000..bb7ca57
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/LICENSE
@@ -0,0 +1,14 @@
+Copyright (c) 2016 Mikkel F. Jørgensen, dvide.com
+Some files also Copyright author of MathGeoLib (https://github.com/juj)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License. http://www.apache.org/licenses/LICENSE-2.0
diff --git a/Source/library/FBSUtil/flatcc/portable/README.md b/Source/library/FBSUtil/flatcc/portable/README.md
new file mode 100644
index 0000000..f6038cb
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/README.md
@@ -0,0 +1,54 @@
+A small library for adding C11 compatibility to older C compilers, but
+only a small highly useful subset such as static assertions, inline
+functions and alignment.
+
+Many compilers already have the required functionality but named
+slightly different.
+
+In addition, compatibility with the Linux `` system file is
+provided, and "punaligned.h" is provided for unaligned memory reads
+which in part depends on endian support.
+
+The library also provides fast integer printing and floating point
+printing and parsing optionally using the grisu3 algorithm, but can fall
+back to strtod and related. The `pgrisu3` folder is header only and
+excludes test cases found in the main grisu3 project the files were
+extracted from.
+
+Integer conversion is not just an optimization. It is more difficult
+than it would appear to portably parse an integer of known size such as
+`uint64_t` up to at most n bytes which is needed for safe parsing. At
+the same time, the sometimes significant performance gains warrants
+custom implementations that might as well be done once and for all.
+
+Files can be included individually, or portable.h may be included to get
+all functionality. If the compiler is C11 compliant, portable.h will not
+include anything, except: it will provide a patch for static assertions
+which clang does not fully support in all versions even with C11 flagged.
+
+The grisu3 header files are the runtime files for the Grisu3 floating
+point conversion to/from text C port. Test coverage is provided separately.
+This library can be used indirectly via pparsefp.h and pprintfp.h.
+
+The `pstatic_assert.h` file is often needed on C11 systems because the
+compiler and standard library may support `_Static_assert` without
+`static_assert`. For compilers without `_Static_assert`, a unique
+identifier is needed for each assertion. This is done non-standard with
+the `__COUNTER__` macro, but has a fallback to `pstatic_assert_scope.h`
+for systems witout the `__COUNTER__` macro. Because of this fallback,
+`pstatic_assert.h` needs to be included in every file using
+`static_assert` in order to increment a scope counter, otherwise there
+is a risk of assert identifier conflicts when `static_assert` happen on
+the same line in different files.
+
+The `paligned_alloc.h` file implements the non-standard `aligned_free`
+to match the C11 standard `aligned_alloc` call. `aligned_free` is
+normally equivalent to `free`, but not on systems where `aligned_free`
+cannot be implemented using a system provived `free` call. Use of
+`aligned_free` is thus optional on some systems, but using it increases
+general portablity at the cost of pure C11 compatibility.
+
+IMPORTANT NOTE: this library is not widely tested. It is intended to be
+a starting point. Each use case should test on relevant target platforms
+and if relevant send patches upstream.
+
diff --git a/Source/library/FBSUtil/flatcc/portable/grisu3_math.h b/Source/library/FBSUtil/flatcc/portable/grisu3_math.h
new file mode 100644
index 0000000..d9c6fee
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/grisu3_math.h
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2016 Mikkel F. Jørgensen, dvide.com
+ * Copyright author of MathGeoLib (https://github.com/juj)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. http://www.apache.org/licenses/LICENSE-2.0
+ */
+
+/* 2016-02-02: Updated by mikkelfj
+ *
+ * Extracted from MatGeoLib grisu3.c, Apache 2.0 license, and extended.
+ *
+ * This file is usually include via grisu3_print.h or grisu3_parse.h.
+ *
+ * The original MatGeoLib dtoa_grisu3 implementation is largely
+ * unchanged except for the uint64 to double cast. The remaining changes
+ * are file structure, name changes, and new additions for parsing:
+ *
+ * - Split into header files only:
+ * grisu3_math.h, grisu3_print.h, (added grisu3_parse.h)
+ *
+ * - names prefixed with grisu3_, grisu3_diy_fp_, GRISU3_.
+ * - added static to all functions.
+ * - disabled clang unused function warnings.
+ * - guarded to allow for alternative impl.
+ * - added extra numeric constants needed for parsing.
+ * - added dec_pow, cast_double_from_diy_fp.
+ * - changed some function names for consistency.
+ * - moved printing specific grisu3 functions to grisu3_print.h.
+ * - changed double to uint64 cast to avoid aliasing.
+ * - added new grisu3_parse.h for parsing doubles.
+ * - grisu3_print_double (dtoa_grisu3) format .1 as 0.1 needed for valid JSON output
+ * and grisu3_parse_double wouldn't consume it.
+ * - grsu3_print_double changed formatting to prefer 0.012 over 1.2e-2.
+ *
+ * These changes make it possible to include the files as headers only
+ * in other software libraries without risking name conflicts, and to
+ * extend the implementation with a port of Googles Double Conversion
+ * strtod functionality for parsing doubles.
+ *
+ * Extracted from: rev. 915501a / Dec 22, 2015
+ *
+ * MathGeoLib License: http://www.apache.org/licenses/LICENSE-2.0.html
+ */
+
+#ifndef GRISU3_MATH_H
+#define GRISU3_MATH_H
+
+/* Guarded to allow inclusion of pstdint.h first, if stdint.h is not supported. */
+#ifndef UINT8_MAX
+#include /* uint64_t etc. */
+#endif
+#include /* assert */
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer */
+#endif
+
+#define GRISU3_D64_SIGN 0x8000000000000000ULL
+#define GRISU3_D64_EXP_MASK 0x7FF0000000000000ULL
+#define GRISU3_D64_FRACT_MASK 0x000FFFFFFFFFFFFFULL
+#define GRISU3_D64_IMPLICIT_ONE 0x0010000000000000ULL
+#define GRISU3_D64_EXP_POS 52
+#define GRISU3_D64_EXP_BIAS 1075
+#define GRISU3_D64_DENORM_EXP (-GRISU3_D64_EXP_BIAS + 1)
+#define GRISU3_DIY_FP_FRACT_SIZE 64
+#define GRISU3_D_1_LOG2_10 0.30102999566398114 /* 1 / lg(10) */
+#define GRISU3_MIN_TARGET_EXP -60
+#define GRISU3_MASK32 0xFFFFFFFFULL
+#define GRISU3_MIN_CACHED_EXP -348
+#define GRISU3_MAX_CACHED_EXP 340
+#define GRISU3_CACHED_EXP_STEP 8
+#define GRISU3_D64_MAX_DEC_EXP 309
+#define GRISU3_D64_MIN_DEC_EXP -324
+#define GRISU3_D64_INF GRISU3_D64_EXP_MASK
+
+#define GRISU3_MIN(x,y) ((x) <= (y) ? (x) : (y))
+#define GRISU3_MAX(x,y) ((x) >= (y) ? (x) : (y))
+
+
+typedef struct grisu3_diy_fp
+{
+ uint64_t f;
+ int e;
+} grisu3_diy_fp_t;
+
+typedef struct grisu3_diy_fp_power
+{
+ uint64_t fract;
+ int16_t b_exp, d_exp;
+} grisu3_diy_fp_power_t;
+
+typedef union {
+ uint64_t u64;
+ double d64;
+} grisu3_cast_double_t;
+
+static uint64_t grisu3_cast_uint64_from_double(double d)
+{
+ grisu3_cast_double_t cd;
+ cd.d64 = d;
+ return cd.u64;
+}
+
+static double grisu3_cast_double_from_uint64(uint64_t u)
+{
+ grisu3_cast_double_t cd;
+ cd.u64 = u;
+ return cd.d64;
+}
+
+#define grisu3_double_infinity grisu3_cast_double_from_uint64(GRISU3_D64_INF)
+#define grisu3_double_nan grisu3_cast_double_from_uint64(GRISU3_D64_INF + 1)
+
+static const grisu3_diy_fp_power_t grisu3_diy_fp_pow_cache[] =
+{
+ { 0xfa8fd5a0081c0288ULL, -1220, -348 },
+ { 0xbaaee17fa23ebf76ULL, -1193, -340 },
+ { 0x8b16fb203055ac76ULL, -1166, -332 },
+ { 0xcf42894a5dce35eaULL, -1140, -324 },
+ { 0x9a6bb0aa55653b2dULL, -1113, -316 },
+ { 0xe61acf033d1a45dfULL, -1087, -308 },
+ { 0xab70fe17c79ac6caULL, -1060, -300 },
+ { 0xff77b1fcbebcdc4fULL, -1034, -292 },
+ { 0xbe5691ef416bd60cULL, -1007, -284 },
+ { 0x8dd01fad907ffc3cULL, -980, -276 },
+ { 0xd3515c2831559a83ULL, -954, -268 },
+ { 0x9d71ac8fada6c9b5ULL, -927, -260 },
+ { 0xea9c227723ee8bcbULL, -901, -252 },
+ { 0xaecc49914078536dULL, -874, -244 },
+ { 0x823c12795db6ce57ULL, -847, -236 },
+ { 0xc21094364dfb5637ULL, -821, -228 },
+ { 0x9096ea6f3848984fULL, -794, -220 },
+ { 0xd77485cb25823ac7ULL, -768, -212 },
+ { 0xa086cfcd97bf97f4ULL, -741, -204 },
+ { 0xef340a98172aace5ULL, -715, -196 },
+ { 0xb23867fb2a35b28eULL, -688, -188 },
+ { 0x84c8d4dfd2c63f3bULL, -661, -180 },
+ { 0xc5dd44271ad3cdbaULL, -635, -172 },
+ { 0x936b9fcebb25c996ULL, -608, -164 },
+ { 0xdbac6c247d62a584ULL, -582, -156 },
+ { 0xa3ab66580d5fdaf6ULL, -555, -148 },
+ { 0xf3e2f893dec3f126ULL, -529, -140 },
+ { 0xb5b5ada8aaff80b8ULL, -502, -132 },
+ { 0x87625f056c7c4a8bULL, -475, -124 },
+ { 0xc9bcff6034c13053ULL, -449, -116 },
+ { 0x964e858c91ba2655ULL, -422, -108 },
+ { 0xdff9772470297ebdULL, -396, -100 },
+ { 0xa6dfbd9fb8e5b88fULL, -369, -92 },
+ { 0xf8a95fcf88747d94ULL, -343, -84 },
+ { 0xb94470938fa89bcfULL, -316, -76 },
+ { 0x8a08f0f8bf0f156bULL, -289, -68 },
+ { 0xcdb02555653131b6ULL, -263, -60 },
+ { 0x993fe2c6d07b7facULL, -236, -52 },
+ { 0xe45c10c42a2b3b06ULL, -210, -44 },
+ { 0xaa242499697392d3ULL, -183, -36 },
+ { 0xfd87b5f28300ca0eULL, -157, -28 },
+ { 0xbce5086492111aebULL, -130, -20 },
+ { 0x8cbccc096f5088ccULL, -103, -12 },
+ { 0xd1b71758e219652cULL, -77, -4 },
+ { 0x9c40000000000000ULL, -50, 4 },
+ { 0xe8d4a51000000000ULL, -24, 12 },
+ { 0xad78ebc5ac620000ULL, 3, 20 },
+ { 0x813f3978f8940984ULL, 30, 28 },
+ { 0xc097ce7bc90715b3ULL, 56, 36 },
+ { 0x8f7e32ce7bea5c70ULL, 83, 44 },
+ { 0xd5d238a4abe98068ULL, 109, 52 },
+ { 0x9f4f2726179a2245ULL, 136, 60 },
+ { 0xed63a231d4c4fb27ULL, 162, 68 },
+ { 0xb0de65388cc8ada8ULL, 189, 76 },
+ { 0x83c7088e1aab65dbULL, 216, 84 },
+ { 0xc45d1df942711d9aULL, 242, 92 },
+ { 0x924d692ca61be758ULL, 269, 100 },
+ { 0xda01ee641a708deaULL, 295, 108 },
+ { 0xa26da3999aef774aULL, 322, 116 },
+ { 0xf209787bb47d6b85ULL, 348, 124 },
+ { 0xb454e4a179dd1877ULL, 375, 132 },
+ { 0x865b86925b9bc5c2ULL, 402, 140 },
+ { 0xc83553c5c8965d3dULL, 428, 148 },
+ { 0x952ab45cfa97a0b3ULL, 455, 156 },
+ { 0xde469fbd99a05fe3ULL, 481, 164 },
+ { 0xa59bc234db398c25ULL, 508, 172 },
+ { 0xf6c69a72a3989f5cULL, 534, 180 },
+ { 0xb7dcbf5354e9beceULL, 561, 188 },
+ { 0x88fcf317f22241e2ULL, 588, 196 },
+ { 0xcc20ce9bd35c78a5ULL, 614, 204 },
+ { 0x98165af37b2153dfULL, 641, 212 },
+ { 0xe2a0b5dc971f303aULL, 667, 220 },
+ { 0xa8d9d1535ce3b396ULL, 694, 228 },
+ { 0xfb9b7cd9a4a7443cULL, 720, 236 },
+ { 0xbb764c4ca7a44410ULL, 747, 244 },
+ { 0x8bab8eefb6409c1aULL, 774, 252 },
+ { 0xd01fef10a657842cULL, 800, 260 },
+ { 0x9b10a4e5e9913129ULL, 827, 268 },
+ { 0xe7109bfba19c0c9dULL, 853, 276 },
+ { 0xac2820d9623bf429ULL, 880, 284 },
+ { 0x80444b5e7aa7cf85ULL, 907, 292 },
+ { 0xbf21e44003acdd2dULL, 933, 300 },
+ { 0x8e679c2f5e44ff8fULL, 960, 308 },
+ { 0xd433179d9c8cb841ULL, 986, 316 },
+ { 0x9e19db92b4e31ba9ULL, 1013, 324 },
+ { 0xeb96bf6ebadf77d9ULL, 1039, 332 },
+ { 0xaf87023b9bf0ee6bULL, 1066, 340 }
+};
+
+/* Avoid dependence on lib math to get (int)ceil(v) */
+static int grisu3_iceil(double v)
+{
+ int k = (int)v;
+ if (v < 0) return k;
+ return v - k == 0 ? k : k + 1;
+}
+
+static int grisu3_diy_fp_cached_pow(int exp, grisu3_diy_fp_t *p)
+{
+ int k = grisu3_iceil((exp+GRISU3_DIY_FP_FRACT_SIZE-1) * GRISU3_D_1_LOG2_10);
+ int i = (k-GRISU3_MIN_CACHED_EXP-1) / GRISU3_CACHED_EXP_STEP + 1;
+ p->f = grisu3_diy_fp_pow_cache[i].fract;
+ p->e = grisu3_diy_fp_pow_cache[i].b_exp;
+ return grisu3_diy_fp_pow_cache[i].d_exp;
+}
+
+static grisu3_diy_fp_t grisu3_diy_fp_minus(grisu3_diy_fp_t x, grisu3_diy_fp_t y)
+{
+ grisu3_diy_fp_t d; d.f = x.f - y.f; d.e = x.e;
+ assert(x.e == y.e && x.f >= y.f);
+ return d;
+}
+
+static grisu3_diy_fp_t grisu3_diy_fp_multiply(grisu3_diy_fp_t x, grisu3_diy_fp_t y)
+{
+ uint64_t a, b, c, d, ac, bc, ad, bd, tmp;
+ grisu3_diy_fp_t r;
+ a = x.f >> 32; b = x.f & GRISU3_MASK32;
+ c = y.f >> 32; d = y.f & GRISU3_MASK32;
+ ac = a*c; bc = b*c;
+ ad = a*d; bd = b*d;
+ tmp = (bd >> 32) + (ad & GRISU3_MASK32) + (bc & GRISU3_MASK32);
+ tmp += 1U << 31; /* round */
+ r.f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32);
+ r.e = x.e + y.e + 64;
+ return r;
+}
+
+static grisu3_diy_fp_t grisu3_diy_fp_normalize(grisu3_diy_fp_t n)
+{
+ assert(n.f != 0);
+ while(!(n.f & 0xFFC0000000000000ULL)) { n.f <<= 10; n.e -= 10; }
+ while(!(n.f & GRISU3_D64_SIGN)) { n.f <<= 1; --n.e; }
+ return n;
+}
+
+static grisu3_diy_fp_t grisu3_cast_diy_fp_from_double(double d)
+{
+ grisu3_diy_fp_t fp;
+ uint64_t u64 = grisu3_cast_uint64_from_double(d);
+ if (!(u64 & GRISU3_D64_EXP_MASK)) { fp.f = u64 & GRISU3_D64_FRACT_MASK; fp.e = 1 - GRISU3_D64_EXP_BIAS; }
+ else { fp.f = (u64 & GRISU3_D64_FRACT_MASK) + GRISU3_D64_IMPLICIT_ONE; fp.e = (int)((u64 & GRISU3_D64_EXP_MASK) >> GRISU3_D64_EXP_POS) - GRISU3_D64_EXP_BIAS; }
+ return fp;
+}
+
+static double grisu3_cast_double_from_diy_fp(grisu3_diy_fp_t n)
+{
+ const uint64_t hidden_bit = GRISU3_D64_IMPLICIT_ONE;
+ const uint64_t frac_mask = GRISU3_D64_FRACT_MASK;
+ const int denorm_exp = GRISU3_D64_DENORM_EXP;
+ const int exp_bias = GRISU3_D64_EXP_BIAS;
+ const int exp_pos = GRISU3_D64_EXP_POS;
+
+ grisu3_diy_fp_t v = n;
+ uint64_t e_biased;
+
+ while (v.f > hidden_bit + frac_mask) {
+ v.f >>= 1;
+ ++v.e;
+ }
+ if (v.e < denorm_exp) {
+ return 0.0;
+ }
+ while (v.e > denorm_exp && (v.f & hidden_bit) == 0) {
+ v.f <<= 1;
+ --v.e;
+ }
+ if (v.e == denorm_exp && (v.f & hidden_bit) == 0) {
+ e_biased = 0;
+ } else {
+ e_biased = (uint64_t)(v.e + exp_bias);
+ }
+ return grisu3_cast_double_from_uint64((v.f & frac_mask) | (e_biased << exp_pos));
+}
+
+/* pow10_cache[i] = 10^(i-1) */
+static const unsigned int grisu3_pow10_cache[] = { 0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
+
+static int grisu3_largest_pow10(uint32_t n, int n_bits, uint32_t *power)
+{
+ int guess = ((n_bits + 1) * 1233 >> 12) + 1/*skip first entry*/;
+ if (n < grisu3_pow10_cache[guess]) --guess; /* We don't have any guarantees that 2^n_bits <= n. */
+ *power = grisu3_pow10_cache[guess];
+ return guess;
+}
+
+#endif /* GRISU3_MATH_H */
diff --git a/Source/library/FBSUtil/flatcc/portable/grisu3_parse.h b/Source/library/FBSUtil/flatcc/portable/grisu3_parse.h
new file mode 100644
index 0000000..c00eea2
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/grisu3_parse.h
@@ -0,0 +1,575 @@
+/*
+ * Copyright (c) 2016 Mikkel F. Jørgensen, dvide.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. http://www.apache.org/licenses/LICENSE-2.0
+ */
+
+/*
+ * Port of parts of Google Double Conversion strtod functionality
+ * but with fallback to strtod instead of a bignum implementation.
+ *
+ * Based on grisu3 math from MathGeoLib.
+ *
+ * See also grisu3_math.h comments.
+ */
+
+#ifndef GRISU3_PARSE_H
+#define GRISU3_PARSE_H
+
+#ifndef UINT8_MAX
+#include
+#endif
+
+#include
+#include
+#include
+
+#include "grisu3_math.h"
+
+/*
+ * The maximum number characters a valid number may contain. The parse
+ * fails if the input length is longer but the character after max len
+ * was part of the number.
+ *
+ * The length should not be set too high because it protects against
+ * overflow in the exponent part derived from the input length.
+ */
+#define GRISU3_NUM_MAX_LEN 1000
+
+/*
+ * The lightweight "portable" C library recognizes grisu3 support if
+ * included first.
+ */
+#define grisu3_parse_double_is_defined 1
+
+/*
+ * Disable to compare performance and to test diy_fp algorithm in
+ * broader range.
+ */
+#define GRISU3_PARSE_FAST_CASE
+
+/* May result in a one off error, otherwise when uncertain, fall back to strtod. */
+//#define GRISU3_PARSE_ALLOW_ERROR
+
+
+/*
+ * The dec output exponent jumps in 8, so the result is offset at most
+ * by 7 when the input is within range.
+ */
+static int grisu3_diy_fp_cached_dec_pow(int d_exp, grisu3_diy_fp_t *p)
+{
+ const int cached_offset = -GRISU3_MIN_CACHED_EXP;
+ const int d_exp_dist = GRISU3_CACHED_EXP_STEP;
+ int i, a_exp;
+
+ assert(GRISU3_MIN_CACHED_EXP <= d_exp);
+ assert(d_exp < GRISU3_MAX_CACHED_EXP + d_exp_dist);
+
+ i = (d_exp + cached_offset) / d_exp_dist;
+ a_exp = grisu3_diy_fp_pow_cache[i].d_exp;
+ p->f = grisu3_diy_fp_pow_cache[i].fract;
+ p->e = grisu3_diy_fp_pow_cache[i].b_exp;
+
+ assert(a_exp <= d_exp);
+ assert(d_exp < a_exp + d_exp_dist);
+
+ return a_exp;
+}
+
+/*
+ * Ported from google double conversion strtod using
+ * MathGeoLibs diy_fp functions for grisu3 in C.
+ *
+ * ulp_half_error is set if needed to trunacted non-zero trialing
+ * characters.
+ *
+ * The actual value we need to encode is:
+ *
+ * (sign ? -1 : 1) * fraction * 2 ^ (exponent - fraction_exp)
+ * where exponent is the base 10 exponent assuming the decimal point is
+ * after the first digit. fraction_exp is the base 10 magnitude of the
+ * fraction or number of significant digits - 1.
+ *
+ * If the exponent is between 0 and 22 and the fraction is encoded in
+ * the lower 53 bits (the largest bit is implicit in a double, but not
+ * in this fraction), then the value can be trivially converted to
+ * double without loss of precision. If the fraction was in fact
+ * multiplied by trailing zeroes that we didn't convert to exponent,
+ * we there are larger values the 53 bits that can also be encoded
+ * trivially - but then it is better to handle this during parsing
+ * if it is worthwhile. We do not optimize for this here, because it
+ * can be done in a simple check before calling, and because it might
+ * not be worthwile to do at all since it cery likely will fail for
+ * numbers printed to be convertible back to double without loss.
+ *
+ * Returns 0 if conversion was not exact. In that case the vale is
+ * either one smaller than the correct one, or the correct one.
+ *
+ * Exponents must be range protected before calling otherwise cached
+ * powers will blow up.
+ *
+ * Google Double Conversion seems to prefer the following notion:
+ *
+ * x >= 10^309 => +Inf
+ * x <= 10^-324 => 0,
+ *
+ * max double: HUGE_VAL = 1.7976931348623157 * 10^308
+ * min double: 4.9406564584124654 * 10^-324
+ *
+ * Values just below or above min/max representable number
+ * may round towards large/small non-Inf/non-neg values.
+ *
+ * but `strtod` seems to return +/-HUGE_VAL on overflow?
+ */
+int grisu3_diy_fp_encode_double(uint64_t fraction, int exponent, int fraction_exp, int ulp_half_error, double *result)
+{
+ /*
+ * Error is measures in fractions of integers, so we scale up to get
+ * some resolution to represent error expressions.
+ */
+ const int log2_error_one = 3;
+ const int error_one = 1 << log2_error_one;
+ const int denorm_exp = GRISU3_D64_DENORM_EXP;
+ const uint64_t hidden_bit = GRISU3_D64_IMPLICIT_ONE;
+ const int diy_size = GRISU3_DIY_FP_FRACT_SIZE;
+ const int max_digits = 19;
+
+ int error = ulp_half_error ? error_one / 2 : 0;
+ int d_exp = (exponent - fraction_exp);
+ int a_exp;
+ int o_exp;
+ grisu3_diy_fp_t v = { fraction, 0 };
+ grisu3_diy_fp_t cp;
+ grisu3_diy_fp_t rounded;
+ int mag;
+ int prec;
+ int prec_bits;
+ int half_way;
+
+ /* When fractions in a double aren't stored with implicit msb fraction bit. */
+
+ /* Shift fraction to msb. */
+ v = grisu3_diy_fp_normalize(v);
+ /* The half point error moves up while the exponent moves down. */
+ error <<= -v.e;
+
+ a_exp = grisu3_diy_fp_cached_dec_pow(d_exp, &cp);
+
+ /* Interpolate between cached powers at distance 8. */
+ if (a_exp != d_exp) {
+ int adj_exp = d_exp - a_exp - 1;
+ static grisu3_diy_fp_t cp_10_lut[] = {
+ { 0xa000000000000000ULL, -60 },
+ { 0xc800000000000000ULL, -57 },
+ { 0xfa00000000000000ULL, -54 },
+ { 0x9c40000000000000ULL, -50 },
+ { 0xc350000000000000ULL, -47 },
+ { 0xf424000000000000ULL, -44 },
+ { 0x9896800000000000ULL, -40 },
+ };
+ assert(adj_exp >= 0 && adj_exp < 7);
+ v = grisu3_diy_fp_multiply(v, cp_10_lut[adj_exp]);
+
+ /* 20 decimal digits won't always fit in 64 bit.
+ * (`fraction_exp` is one less than significant decimal
+ * digits in fraction, e.g. 1 * 10e0).
+ * If we cannot fit, introduce 1/2 ulp error
+ * (says double conversion reference impl.) */
+ if (1 + fraction_exp + adj_exp > max_digits) {
+ error += error_one / 2;
+ }
+ }
+
+ v = grisu3_diy_fp_multiply(v, cp);
+ /*
+ * Google double conversion claims that:
+ *
+ * The error introduced by a multiplication of a*b equals
+ * error_a + error_b + error_a*error_b/2^64 + 0.5
+ * Substituting a with 'input' and b with 'cached_power' we have
+ * error_b = 0.5 (all cached powers have an error of less than 0.5 ulp),
+ * error_ab = 0 or 1 / error_oner > error_a*error_b/ 2^64
+ *
+ * which in our encoding becomes:
+ * error_a = error_one/2
+ * error_ab = 1 / error_one (rounds up to 1 if error != 0, or 0 * otherwise)
+ * fixed_error = error_one/2
+ *
+ * error += error_a + fixed_error + (error ? 1 : 0)
+ *
+ * (this isn't entirely clear, but that is as close as we get).
+ */
+ error += error_one + (error ? 1 : 0);
+
+ o_exp = v.e;
+ v = grisu3_diy_fp_normalize(v);
+ /* Again, if we shift the significant bits, the error moves along. */
+ error <<= o_exp - v.e;
+
+ /*
+ * The value `v` is bounded by 2^mag which is 64 + v.e. because we
+ * just normalized it by shifting towards msb.
+ */
+ mag = diy_size + v.e;
+
+ /* The effective magnitude of the IEEE double representation. */
+ mag = mag >= diy_size + denorm_exp ? diy_size : mag <= denorm_exp ? 0 : mag - denorm_exp;
+ prec = diy_size - mag;
+ if (prec + log2_error_one >= diy_size) {
+ int e_scale = prec + log2_error_one - diy_size - 1;
+ v.f >>= e_scale;
+ v.e += e_scale;
+ error = (error >> e_scale) + 1 + error_one;
+ prec -= e_scale;
+ }
+ rounded.f = v.f >> prec;
+ rounded.e = v.e + prec;
+ prec_bits = (v.f & ((uint64_t)1 << (prec - 1))) * error_one;
+ half_way = ((uint64_t)1 << (prec - 1)) * error_one;
+ if (prec >= half_way + error) {
+ rounded.f++;
+ /* Prevent overflow. */
+ if (rounded.f & (hidden_bit << 1)) {
+ rounded.f >>= 1;
+ rounded.e += 1;
+ }
+ }
+ *result = grisu3_cast_double_from_diy_fp(rounded);
+ return half_way - error >= prec_bits || prec_bits >= half_way + error;
+}
+
+/*
+ * `end` is unchanged if number is handled natively, or it is the result
+ * of strtod parsing in case of fallback.
+ */
+static const char *grisu3_encode_double(const char *buf, const char *end, int sign, uint64_t fraction, int exponent, int fraction_exp, int ulp_half_error, double *result)
+{
+ const int max_d_exp = GRISU3_D64_MAX_DEC_EXP;
+ const int min_d_exp = GRISU3_D64_MIN_DEC_EXP;
+ const double infinity = (double)GRISU3_D64_INF;
+
+ char *v_end;
+
+ /* Both for user experience, and to protect internal power table lookups. */
+ if (fraction == 0 || exponent < min_d_exp) {
+ *result = 0.0;
+ goto done;
+ }
+ if (exponent - 1 > max_d_exp) {
+ *result = infinity;
+ goto done;
+ }
+
+ /*
+ * `exponent` is the normalized value, fraction_exp is the size of
+ * the representation in the `fraction value`, or one less than
+ * number of significant digits.
+ *
+ * If the final value can be kept in 53 bits and we can avoid
+ * division, then we can convert to double quite fast.
+ *
+ * ulf_half_error only happens when fraction is maxed out, so
+ * fraction_exp > 22 by definition.
+ *
+ * fraction_exp >= 0 always.
+ *
+ * http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
+ */
+
+
+#ifdef GRISU3_PARSE_FAST_CASE
+ if (fraction < (1ULL << 53) && exponent >= 0 && exponent <= 22) {
+ double v = (double)fraction;
+ /* Multiplying by 1e-k instead of dividing by 1ek results in rounding error. */
+ switch (exponent - fraction_exp) {
+ case -22: v /= 1e22; break;
+ case -21: v /= 1e21; break;
+ case -20: v /= 1e20; break;
+ case -19: v /= 1e19; break;
+ case -18: v /= 1e18; break;
+ case -17: v /= 1e17; break;
+ case -16: v /= 1e16; break;
+ case -15: v /= 1e15; break;
+ case -14: v /= 1e14; break;
+ case -13: v /= 1e13; break;
+ case -12: v /= 1e12; break;
+ case -11: v /= 1e11; break;
+ case -10: v /= 1e10; break;
+ case -9: v /= 1e9; break;
+ case -8: v /= 1e8; break;
+ case -7: v /= 1e7; break;
+ case -6: v /= 1e6; break;
+ case -5: v /= 1e5; break;
+ case -4: v /= 1e4; break;
+ case -3: v /= 1e3; break;
+ case -2: v /= 1e2; break;
+ case -1: v /= 1e1; break;
+ case 0: break;
+ case 1: v *= 1e1; break;
+ case 2: v *= 1e2; break;
+ case 3: v *= 1e3; break;
+ case 4: v *= 1e4; break;
+ case 5: v *= 1e5; break;
+ case 6: v *= 1e6; break;
+ case 7: v *= 1e7; break;
+ case 8: v *= 1e8; break;
+ case 9: v *= 1e9; break;
+ case 10: v *= 1e10; break;
+ case 11: v *= 1e11; break;
+ case 12: v *= 1e12; break;
+ case 13: v *= 1e13; break;
+ case 14: v *= 1e14; break;
+ case 15: v *= 1e15; break;
+ case 16: v *= 1e16; break;
+ case 17: v *= 1e17; break;
+ case 18: v *= 1e18; break;
+ case 19: v *= 1e19; break;
+ case 20: v *= 1e20; break;
+ case 21: v *= 1e21; break;
+ case 22: v *= 1e22; break;
+ }
+ *result = v;
+ goto done;
+ }
+#endif
+
+ if (grisu3_diy_fp_encode_double(fraction, exponent, fraction_exp, ulp_half_error, result)) {
+ goto done;
+ }
+#ifdef GRISU3_PARSE_ALLOW_ERROR
+ goto done;
+#endif
+ *result = strtod(buf, &v_end);
+ if (v_end < end) {
+ return v_end;
+ }
+ return end;
+done:
+ if (sign) {
+ *result = -*result;
+ }
+ return end;
+}
+
+/*
+ * Returns buf if number wasn't matched, or null if number starts ok
+ * but contains invalid content.
+ */
+static const char *grisu3_parse_hex_fp(const char *buf, const char *end, int sign, double *result)
+{
+ (void)buf;
+ (void)end;
+ (void)sign;
+ *result = 0.0;
+ /* Not currently supported. */
+ return buf;
+}
+
+/*
+ * Returns end pointer on success, or null, or buf if start is not a number.
+ * Sets result to 0.0 on error.
+ * Reads up to len + 1 bytes from buffer where len + 1 must not be a
+ * valid part of a number, but all of buf, buf + len need not be a
+ * number. Leading whitespace is NOT valid.
+ * Very small numbers are truncated to +/-0.0 and numerically very large
+ * numbers are returns as +/-infinity.
+ *
+ * A value must not end or begin with '.' (like JSON), but can have
+ * leading zeroes (unlike JSON). A single leading zero followed by
+ * an encoding symbol may or may not be interpreted as a non-decimal
+ * encoding prefix, e.g. 0x, but a leading zero followed by a digit is
+ * NOT interpreted as octal.
+ * A single leading negative sign may appear before digits, but positive
+ * sign is not allowed and space after the sign is not allowed.
+ * At most the first 1000 characters of the input is considered.
+ */
+static const char *grisu3_parse_double(const char *buf, int len, double *result)
+{
+ const char *mark, *k, *end;
+ int sign = 0, esign = 0;
+ uint64_t fraction = 0;
+ int exponent = 0;
+ int ee = 0;
+ int fraction_exp = 0;
+ int ulp_half_error = 0;
+
+ *result = 0.0;
+
+ end = buf + len + 1;
+
+ /* Failsafe for exponent overflow. */
+ if (len > GRISU3_NUM_MAX_LEN) {
+ end = buf + GRISU3_NUM_MAX_LEN + 1;
+ }
+
+ if (buf == end) {
+ return buf;
+ }
+ mark = buf;
+ if (*buf == '-') {
+ ++buf;
+ sign = 1;
+ if (buf == end) {
+ return 0;
+ }
+ }
+ if (*buf == '0') {
+ ++buf;
+ /* | 0x20 is lower case ASCII. */
+ if (buf != end && (*buf | 0x20) == 'x') {
+ k = grisu3_parse_hex_fp(buf, end, sign, result);
+ if (k == buf) {
+ return mark;
+ }
+ return k;
+ }
+ /* Not worthwhile, except for getting the scale of integer part. */
+ while (buf != end && *buf == '0') {
+ ++buf;
+ }
+ } else {
+ if (*buf < '1' || *buf > '9') {
+ /*
+ * If we didn't see a sign, just don't recognize it as
+ * number, otherwise make it an error.
+ */
+ return sign ? 0 : mark;
+ }
+ fraction = *buf++ - '0';
+ }
+ k = buf;
+ /*
+ * We do not catch trailing zeroes when there is no decimal point.
+ * This misses an opportunity for moving the exponent down into the
+ * fast case. But it is unlikely to be worthwhile as it complicates
+ * parsing.
+ */
+ while (buf != end && *buf >= '0' && *buf <= '9') {
+ if (fraction >= UINT64_MAX / 10) {
+ fraction += *buf >= '5';
+ ulp_half_error = 1;
+ break;
+ }
+ fraction = fraction * 10 + *buf++ - '0';
+ }
+ fraction_exp = (int)(buf - k);
+ /* Skip surplus digits. Trailing zero does not introduce error. */
+ while (buf != end && *buf == '0') {
+ ++exponent;
+ ++buf;
+ }
+ if (buf != end && *buf >= '1' && *buf <= '9') {
+ ulp_half_error = 1;
+ ++exponent;
+ ++buf;
+ while (buf != end && *buf >= '0' && *buf <= '9') {
+ ++exponent;
+ ++buf;
+ }
+ }
+ if (buf != end && *buf == '.') {
+ ++buf;
+ k = buf;
+ if (*buf < '0' || *buf > '9') {
+ /* We don't accept numbers without leading or trailing digit. */
+ return 0;
+ }
+ while (buf != end && *buf >= '0' && *buf <= '9') {
+ if (fraction >= UINT64_MAX / 10) {
+ if (!ulp_half_error) {
+ fraction += *buf >= '5';
+ ulp_half_error = 1;
+ }
+ break;
+ }
+ fraction = fraction * 10 + *buf++ - '0';
+ --exponent;
+ }
+ fraction_exp += (int)(buf - k);
+ while (buf != end && *buf == '0') {
+ ++exponent;
+ ++buf;
+ }
+ if (buf != end && *buf >= '1' && *buf <= '9') {
+ ulp_half_error = 1;
+ ++buf;
+ while (buf != end && *buf >= '0' && *buf <= '9') {
+ ++buf;
+ }
+ }
+ }
+ /*
+ * Normalized exponent e.g: 1.23434e3 with fraction = 123434,
+ * fraction_exp = 5, exponent = 3.
+ * So value = fraction * 10^(exponent - fraction_exp)
+ */
+ exponent += fraction_exp;
+ if (buf != end && (*buf | 0x20) == 'e') {
+ if (end - buf < 2) {
+ return 0;
+ }
+ ++buf;
+ if (*buf == '+') {
+ ++buf;
+ if (buf == end) {
+ return 0;
+ }
+ } else if (*buf == '-') {
+ esign = 1;
+ ++buf;
+ if (buf == end) {
+ return 0;
+ }
+ }
+ if (*buf < '0' || *buf > '9') {
+ return 0;
+ }
+ ee = *buf++ - '0';
+ while (buf != end && *buf >= '0' && *buf <= '9') {
+ /*
+ * This test impacts performance and we do not need an
+ * exact value just one large enough to dominate the fraction_exp.
+ * Subsequent handling maps large absolute ee to 0 or infinity.
+ */
+ if (ee <= 0x7fff) {
+ ee = ee * 10 + *buf - '0';
+ }
+ ++buf;
+ }
+ }
+ exponent = exponent + (esign ? -ee : ee);
+
+ /*
+ * Exponent is now a base 10 normalized exponent so the absolute value
+ * is less the 10^(exponent + 1) for positive exponents. For
+ * denormalized doubles (using 11 bit exponent 0 with a fraction
+ * shiftet down, extra small numbers can be achieved.
+ *
+ * https://en.wikipedia.org/wiki/Double-precision_floating-point_format
+ *
+ * 10^-324 holds the smallest normalized exponent (but not value) and
+ * 10^308 holds the largest exponent. Internally our lookup table is
+ * only safe to use within a range slightly larger than this.
+ * Externally, a slightly larger/smaller value represents NaNs which
+ * are technically also possible to store as a number.
+ *
+ */
+
+ /* This also protects strod fallback parsing. */
+ if (buf == end) {
+ return 0;
+ }
+ return grisu3_encode_double(mark, buf, sign, fraction, exponent, fraction_exp, ulp_half_error, result);
+}
+
+#endif /* GRISU3_PARSE_H */
diff --git a/Source/library/FBSUtil/flatcc/portable/grisu3_print.h b/Source/library/FBSUtil/flatcc/portable/grisu3_print.h
new file mode 100644
index 0000000..fce20f0
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/grisu3_print.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2016 Mikkel F. Jørgensen, dvide.com
+ * Copyright author of MathGeoLib (https://github.com/juj)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. http://www.apache.org/licenses/LICENSE-2.0
+ */
+
+/*
+ * Extracted from MathGeoLib.
+ *
+ * mikkelfj:
+ * - Fixed final output when printing single digit negative exponent to
+ * have leading zero (important for JSON).
+ * - Changed formatting to prefer 0.012 over 1.2-e-2.
+ *
+ * Large portions of the original grisu3.c file has been moved to
+ * grisu3_math.h, the rest is placed here.
+ *
+ * See also comments in grisu3_math.h.
+ *
+ * MatGeoLib grisu3.c comment:
+ *
+ * This file is part of an implementation of the "grisu3" double to string
+ * conversion algorithm described in the research paper
+ *
+ * "Printing Floating-Point Numbers Quickly And Accurately with Integers"
+ * by Florian Loitsch, available at
+ * http://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
+ */
+
+#ifndef GRISU3_PRINT_H
+#define GRISU3_PRINT_H
+
+#include /* sprintf */
+#include /* assert */
+
+#include "grisu3_math.h"
+
+/*
+ * The lightweight "portable" C library recognizes grisu3 support if
+ * included first.
+ */
+#define grisu3_print_double_is_defined 1
+
+/*
+ * Not sure we have an exact definition, but we get up to 23
+ * emperically. There is some math ensuring it does not go awol though,
+ * like 18 digits + exponent or so.
+ * This max should be safe size buffer for printing, including zero term.
+ */
+#define GRISU3_PRINT_MAX 30
+
+static int grisu3_round_weed(char *buffer, int len, uint64_t wp_W, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t ulp)
+{
+ uint64_t wp_Wup = wp_W - ulp;
+ uint64_t wp_Wdown = wp_W + ulp;
+ while(rest < wp_Wup && delta - rest >= ten_kappa
+ && (rest + ten_kappa < wp_Wup || wp_Wup - rest >= rest + ten_kappa - wp_Wup))
+ {
+ --buffer[len-1];
+ rest += ten_kappa;
+ }
+ if (rest < wp_Wdown && delta - rest >= ten_kappa
+ && (rest + ten_kappa < wp_Wdown || wp_Wdown - rest > rest + ten_kappa - wp_Wdown))
+ return 0;
+
+ return 2*ulp <= rest && rest <= delta - 4*ulp;
+}
+
+static int grisu3_digit_gen(grisu3_diy_fp_t low, grisu3_diy_fp_t w, grisu3_diy_fp_t high, char *buffer, int *length, int *kappa)
+{
+ uint64_t unit = 1;
+ grisu3_diy_fp_t too_low = { low.f - unit, low.e };
+ grisu3_diy_fp_t too_high = { high.f + unit, high.e };
+ grisu3_diy_fp_t unsafe_interval = grisu3_diy_fp_minus(too_high, too_low);
+ grisu3_diy_fp_t one = { 1ULL << -w.e, w.e };
+ uint32_t p1 = (uint32_t)(too_high.f >> -one.e);
+ uint64_t p2 = too_high.f & (one.f - 1);
+ uint32_t div;
+ *kappa = grisu3_largest_pow10(p1, GRISU3_DIY_FP_FRACT_SIZE + one.e, &div);
+ *length = 0;
+
+ while(*kappa > 0)
+ {
+ uint64_t rest;
+ int digit = p1 / div;
+ buffer[*length] = (char)('0' + digit);
+ ++*length;
+ p1 %= div;
+ --*kappa;
+ rest = ((uint64_t)p1 << -one.e) + p2;
+ if (rest < unsafe_interval.f) return grisu3_round_weed(buffer, *length, grisu3_diy_fp_minus(too_high, w).f, unsafe_interval.f, rest, (uint64_t)div << -one.e, unit);
+ div /= 10;
+ }
+
+ for(;;)
+ {
+ int digit;
+ p2 *= 10;
+ unit *= 10;
+ unsafe_interval.f *= 10;
+ /* Integer division by one. */
+ digit = (int)(p2 >> -one.e);
+ buffer[*length] = (char)('0' + digit);
+ ++*length;
+ p2 &= one.f - 1; /* Modulo by one. */
+ --*kappa;
+ if (p2 < unsafe_interval.f) return grisu3_round_weed(buffer, *length, grisu3_diy_fp_minus(too_high, w).f * unit, unsafe_interval.f, p2, one.f, unit);
+ }
+}
+
+static int grisu3(double v, char *buffer, int *length, int *d_exp)
+{
+ int mk, kappa, success;
+ grisu3_diy_fp_t dfp = grisu3_cast_diy_fp_from_double(v);
+ grisu3_diy_fp_t w = grisu3_diy_fp_normalize(dfp);
+
+ /* normalize boundaries */
+ grisu3_diy_fp_t t = { (dfp.f << 1) + 1, dfp.e - 1 };
+ grisu3_diy_fp_t b_plus = grisu3_diy_fp_normalize(t);
+ grisu3_diy_fp_t b_minus;
+ grisu3_diy_fp_t c_mk; /* Cached power of ten: 10^-k */
+ uint64_t u64 = grisu3_cast_uint64_from_double(v);
+ assert(v > 0 && v <= 1.7976931348623157e308); /* Grisu only handles strictly positive finite numbers. */
+ if (!(u64 & GRISU3_D64_FRACT_MASK) && (u64 & GRISU3_D64_EXP_MASK) != 0) { b_minus.f = (dfp.f << 2) - 1; b_minus.e = dfp.e - 2;} /* lower boundary is closer? */
+ else { b_minus.f = (dfp.f << 1) - 1; b_minus.e = dfp.e - 1; }
+ b_minus.f = b_minus.f << (b_minus.e - b_plus.e);
+ b_minus.e = b_plus.e;
+
+ mk = grisu3_diy_fp_cached_pow(GRISU3_MIN_TARGET_EXP - GRISU3_DIY_FP_FRACT_SIZE - w.e, &c_mk);
+
+ w = grisu3_diy_fp_multiply(w, c_mk);
+ b_minus = grisu3_diy_fp_multiply(b_minus, c_mk);
+ b_plus = grisu3_diy_fp_multiply(b_plus, c_mk);
+
+ success = grisu3_digit_gen(b_minus, w, b_plus, buffer, length, &kappa);
+ *d_exp = kappa - mk;
+ return success;
+}
+
+static int grisu3_i_to_str(int val, char *str)
+{
+ int len, i;
+ char *s;
+ char *begin = str;
+ if (val < 0) { *str++ = '-'; val = -val; }
+ s = str;
+
+ for(;;)
+ {
+ int ni = val / 10;
+ int digit = val - ni*10;
+ *s++ = (char)('0' + digit);
+ if (ni == 0)
+ break;
+ val = ni;
+ }
+ *s = '\0';
+ len = (int)(s - str);
+ for(i = 0; i < len/2; ++i)
+ {
+ char ch = str[i];
+ str[i] = str[len-1-i];
+ str[len-1-i] = ch;
+ }
+
+ return (int)(s - begin);
+}
+
+static int grisu3_print_double(double v, char *dst)
+{
+ int d_exp, len, success, decimals, i;
+ uint64_t u64 = grisu3_cast_uint64_from_double(v);
+ char *s2 = dst;
+ assert(dst);
+
+ /* Prehandle NaNs */
+ if ((u64 << 1) > 0xFFE0000000000000ULL) return sprintf(dst, "NaN(%08X%08X)", (uint32_t)(u64 >> 32), (uint32_t)u64);
+ /* Prehandle negative values. */
+ if ((u64 & GRISU3_D64_SIGN) != 0) { *s2++ = '-'; v = -v; u64 ^= GRISU3_D64_SIGN; }
+ /* Prehandle zero. */
+ if (!u64) { *s2++ = '0'; *s2 = '\0'; return (int)(s2 - dst); }
+ /* Prehandle infinity. */
+ if (u64 == GRISU3_D64_EXP_MASK) { *s2++ = 'i'; *s2++ = 'n'; *s2++ = 'f'; *s2 = '\0'; return (int)(s2 - dst); }
+
+ success = grisu3(v, s2, &len, &d_exp);
+ /* If grisu3 was not able to convert the number to a string, then use old sprintf (suboptimal). */
+ if (!success) return sprintf(s2, "%.17g", v) + (int)(s2 - dst);
+
+ /* We now have an integer string of form "151324135" and a base-10 exponent for that number. */
+ /* Next, decide the best presentation for that string by whether to use a decimal point, or the scientific exponent notation 'e'. */
+ /* We don't pick the absolute shortest representation, but pick a balance between readability and shortness, e.g. */
+ /* 1.545056189557677e-308 could be represented in a shorter form */
+ /* 1545056189557677e-323 but that would be somewhat unreadable. */
+ decimals = GRISU3_MIN(-d_exp, GRISU3_MAX(1, len-1));
+
+ /* mikkelfj:
+ * fix zero prefix .1 => 0.1, important for JSON export.
+ * prefer unscientific notation at same length:
+ * -1.2345e-4 over -1.00012345,
+ * -1.0012345 over -1.2345e-3
+ */
+ if (d_exp < 0 && (len + d_exp) > -3 && len <= -d_exp)
+ {
+ /* mikkelfj: fix zero prefix .1 => 0.1, and short exponents 1.3e-2 => 0.013. */
+ memmove(s2 + 2 - d_exp - len, s2, len);
+ s2[0] = '0';
+ s2[1] = '.';
+ for (i = 2; i < 2-d_exp-len; ++i) s2[i] = '0';
+ len += i;
+ }
+ else if (d_exp < 0 && len > 1) /* Add decimal point? */
+ {
+ for(i = 0; i < decimals; ++i) s2[len-i] = s2[len-i-1];
+ s2[len++ - decimals] = '.';
+ d_exp += decimals;
+ /* Need scientific notation as well? */
+ if (d_exp != 0) { s2[len++] = 'e'; len += grisu3_i_to_str(d_exp, s2+len); }
+ }
+ /* Add scientific notation? */
+ else if (d_exp < 0 || d_exp > 2) { s2[len++] = 'e'; len += grisu3_i_to_str(d_exp, s2+len); }
+ /* Add zeroes instead of scientific notation? */
+ else if (d_exp > 0) { while(d_exp-- > 0) s2[len++] = '0'; }
+ s2[len] = '\0'; /* grisu3 doesn't null terminate, so ensure termination. */
+ return (int)(s2+len-dst);
+}
+
+#endif /* GRISU3_PRINT_H */
diff --git a/Source/library/FBSUtil/flatcc/portable/paligned_alloc.h b/Source/library/FBSUtil/flatcc/portable/paligned_alloc.h
new file mode 100644
index 0000000..ed90c30
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/paligned_alloc.h
@@ -0,0 +1,158 @@
+#ifndef PALIGNED_ALLOC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * NOTE: MSVC in general has no aligned alloc function that is
+ * compatible with free and it is not trivial to implement a version
+ * which is. Therefore, to remain portable, end user code needs to
+ * use `aligned_free` which is not part of C11 but defined in this header.
+ *
+ * The same issue is present on some Unix systems not providing
+ * posix_memalign.
+ *
+ * For C11 compliant compilers and compilers with posix_memalign,
+ * it is valid to use free instead of aligned_free with the above
+ * caveats.
+ */
+
+#include
+
+
+/*
+ * Define this to see which version is used so the fallback is not
+ * enganged unnecessarily:
+ *
+ * #define PORTABLE_DEBUG_ALIGNED_ALLOC
+ */
+
+#if !defined(PORTABLE_C11_ALIGNED_ALLOC)
+
+#if defined (__GLIBC__)
+#define PORTABLE_C11_ALIGNED_ALLOC 0
+#elif defined (__clang__)
+#define PORTABLE_C11_ALIGNED_ALLOC 0
+#elif defined(__IBMC__)
+#define PORTABLE_C11_ALIGNED_ALLOC 0
+#elif (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L)
+#define PORTABLE_C11_ALIGNED_ALLOC 1
+#else
+#define PORTABLE_C11_ALIGNED_ALLOC 0
+#endif
+
+#endif /* PORTABLE_C11_ALIGNED_ALLOC */
+
+/* https://linux.die.net/man/3/posix_memalign */
+#if !defined(PORTABLE_POSIX_MEMALIGN)
+/* https://forum.kde.org/viewtopic.php?p=66274 */
+#if (defined _GNU_SOURCE) || ((_XOPEN_SOURCE + 0) >= 600) || (_POSIX_C_SOURCE + 0) >= 200112L
+#define PORTABLE_POSIX_MEMALIGN 1
+#elif (__clang__)
+#define PORTABLE_POSIX_MEMALIGN 1
+#else
+#define PORTABLE_POSIX_MEMALIGN 0
+#endif
+#endif /* PORTABLE_POSIX_MEMALIGN */
+
+/* https://forum.kde.org/viewtopic.php?p=66274 */
+#if (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L)
+/* C11 or newer */
+#include
+#endif
+
+/* C11 or newer */
+#if !defined(aligned_alloc) && !defined(__aligned_alloc_is_defined)
+
+#if PORTABLE_C11_ALIGNED_ALLOC
+#ifdef PORTABLE_DEBUG_ALIGNED_ALLOC
+#error "DEBUG: C11_ALIGNED_ALLOC configured"
+#endif
+#elif defined(_MSC_VER)
+
+/* Aligned _aligned_malloc is not compatible with free. */
+#define aligned_alloc(alignment, size) _aligned_malloc(size, alignment)
+#define aligned_free(p) _aligned_free(p)
+#define __aligned_alloc_is_defined 1
+#define __aligned_free_is_defined 1
+
+#elif PORTABLE_POSIX_MEMALIGN
+
+#if defined(__GNUC__) && __GNUCC__ < 5
+extern int posix_memalign (void **, size_t, size_t);
+#endif
+
+static inline void *__portable_aligned_alloc(size_t alignment, size_t size)
+{
+ int err;
+ void *p = 0;
+
+ if (alignment < sizeof(void *)) {
+ alignment = sizeof(void *);
+ }
+ err = posix_memalign(&p, alignment, size);
+ if (err && p) {
+ free(p);
+ p = 0;
+ }
+ return p;
+}
+
+#ifdef PORTABLE_DEBUG_ALIGNED_ALLOC
+#error "DEBUG: POSIX_MEMALIGN configured"
+#endif
+
+#define aligned_alloc(alignment, size) __portable_aligned_alloc(alignment, size)
+#define aligned_free(p) free(p)
+#define __aligned_alloc_is_defined 1
+#define __aligned_free_is_defined 1
+
+#else
+
+static inline void *__portable_aligned_alloc(size_t alignment, size_t size)
+{
+ char *raw;
+ void *buf;
+ size_t total_size = (size + alignment - 1 + sizeof(void *));
+
+ if (alignment < sizeof(void *)) {
+ alignment = sizeof(void *);
+ }
+ raw = (char *)(size_t)malloc(total_size);
+ buf = raw + alignment - 1 + sizeof(void *);
+ buf = (void *)(((size_t)buf) & ~(alignment - 1));
+ ((void **)buf)[-1] = raw;
+ return buf;
+}
+
+static inline void __portable_aligned_free(void *p)
+{
+ char *raw = ((void **)p)[-1];
+
+ free(raw);
+}
+
+#define aligned_alloc(alignment, size) __portable_aligned_alloc(alignment, size)
+#define aligned_free(p) __portable_aligned_free(p)
+#define __aligned_alloc_is_defined 1
+#define __aligned_free_is_defined 1
+
+#ifdef PORTABLE_DEBUG_ALIGNED_ALLOC
+#error "DEBUG: aligned_alloc malloc fallback configured"
+#endif
+
+#endif
+
+#endif /* aligned_alloc */
+
+#if !defined(aligned_free) && !defined(__aligned_free_is_defined)
+#define aligned_free(p) free(p)
+#define __aligned_free_is_defined 1
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PALIGNED_ALLOC_H */
diff --git a/Source/library/FBSUtil/flatcc/portable/pdiagnostic.h b/Source/library/FBSUtil/flatcc/portable/pdiagnostic.h
new file mode 100644
index 0000000..eb66129
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pdiagnostic.h
@@ -0,0 +1,61 @@
+ /* There is intentionally no include guard in this file. */
+
+
+/*
+ * Usage: optionally disable any of these before including.
+ *
+ * #define PDIAGNOSTIC_IGNORE_UNUSED_FUNCTION
+ * #define PDIAGNOSTIC_IGNORE_UNUSED_VARIABLE
+ * #define PDIAGNOSTIC_IGNORE_UNUSED_PARAMETER
+ * #define PDIAGNOSTIC_IGNORE_UNUSED // all of the above
+ *
+ * #include "pdiagnostic.h"
+ *
+ * Alternatively use #include "pdiagnostic_push/pop.h"
+ */
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4668) /* preprocessor name not defined */
+#endif
+
+#if defined(_MSC_VER) && !defined(PDIAGNOSTIC_AWARE_MSVC)
+#define PDIAGNOSTIC_AWARE_MSVC 1
+#elif defined(__clang__) && !defined(PDIAGNOSTIC_AWARE_CLANG)
+#define PDIAGNOSTIC_AWARE_CLANG 1
+/* Can disable some warnings even if push is not available (gcc-4.2 vs gcc-4.7) */
+#elif ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)) && \
+ !defined(PDIAGNOSTIC_AWARE_GCC)
+#define PDIAGNOSTIC_AWARE_GCC 1
+#endif
+
+#if defined(PDIAGNOSTIC_IGNORE_UNUSED_FUNCTION) || defined(PDIAGNOSTIC_IGNORE_UNUSED)
+#if PDIAGNOSTIC_AWARE_CLANG
+#pragma clang diagnostic ignored "-Wunused-function"
+#elif PDIAGNOSTIC_AWARE_GCC
+#pragma GCC diagnostic ignored "-Wunused-function" */
+#endif
+#endif
+#undef PDIAGNOSTIC_IGNORE_UNUSED_FUNCTION
+
+#if defined(PDIAGNOSTIC_IGNORE_UNUSED_VARIABLE) || defined(PDIAGNOSTIC_IGNORE_UNUSED)
+#if PDIAGNOSTIC_AWARE_MSVC
+#pragma warning(disable: 4101) /* unused local variable */
+#elif PDIAGNOSTIC_AWARE_CLANG
+#pragma clang diagnostic ignored "-Wunused-variable"
+#elif PDIAGNOSTIC_AWARE_GCC
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#endif
+#endif
+#undef PDIAGNOSTIC_IGNORE_UNUSED_VARIABLE
+
+#if defined(PDIAGNOSTIC_IGNORE_UNUSED_PARAMETER) || defined(PDIAGNOSTIC_IGNORE_UNUSED)
+#if PDIAGNOSTIC_AWARE_CLANG
+#pragma clang diagnostic ignored "-Wunused-parameter"
+#elif PDIAGNOSTIC_AWARE_GCC
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+#endif
+#undef PDIAGNOSTIC_IGNORE_UNUSED_PARAMETER
+
+#undef PDIAGNOSTIC_IGNORE_UNUSED
+
diff --git a/Source/library/FBSUtil/flatcc/portable/pdiagnostic_pop.h b/Source/library/FBSUtil/flatcc/portable/pdiagnostic_pop.h
new file mode 100644
index 0000000..b83ec4b
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pdiagnostic_pop.h
@@ -0,0 +1,15 @@
+#ifndef PDIAGNOSTIC_POP_H
+#define PDIAGNOSTIC_POP_H
+
+#if PDIAGNOSTIC_PUSHED_MSVC
+#pragma warning( pop )
+#undef PDIAGNOSTIC_PUSHED_MSVC
+#elif PDIAGNOSTIC_PUSHED_CLANG
+#pragma clang diagnostic pop
+#undef PDIAGNOSTIC_PUSHED_CLANG
+#elif PDIAGNOSTIC_PUSHED_GCC
+#pragma GCC diagnostic pop
+#undef PDIAGNOSTIC_PUSHED_GCC
+#endif
+
+#endif /* PDIAGNOSTIC_POP_H */
diff --git a/Source/library/FBSUtil/flatcc/portable/pdiagnostic_push.h b/Source/library/FBSUtil/flatcc/portable/pdiagnostic_push.h
new file mode 100644
index 0000000..2adc231
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pdiagnostic_push.h
@@ -0,0 +1,46 @@
+#ifndef PDIAGNOSTIC_PUSH_H
+#define PDIAGNOSTIC_PUSH_H
+
+/*
+ * See also comment in "pdiagnostic.h"
+ *
+ * e.g.
+ * #define PDIAGNOSTIC_IGNORE_USED_FUNCTION
+ * #define PDIAGNOSTIC_IGNORE_USED_VARIABLE
+ * #include "pdiagnostic_push"
+ * ...
+ * #include "pdiagnostic_pop.h"
+ *
+ *
+ * or if push pop isn't desired:
+ * #define PDIAGNOSTIC_IGNORE_USED_FUNCTION
+ * #define PDIAGNOSTIC_IGNORE_USED_VARIABLE
+ * #include "pdiagnostic.h"
+ * ...
+ *
+ *
+ *
+ * Some if these warnings cannot be ignored
+ * at the #pragma level, but might in the future.
+ * Use compiler switches like -Wno-unused-function
+ * to work around this.
+ */
+
+#if defined(_MSC_VER)
+#pragma warning( push )
+#define PDIAGNOSTIC_PUSHED_MSVC 1
+#elif defined(__clang__)
+#pragma clang diagnostic push
+#define PDIAGNOSTIC_PUSHED_CLANG 1
+#elif ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+#pragma GCC diagnostic push
+#define PDIAGNOSTIC_PUSHED_GCC 1
+#endif
+
+#endif /* PDIAGNOSTIC_PUSH_H */
+
+/*
+ * We cannot handle nested push, but we can add to the parent context
+ * so keep this outside the header include guard.
+ */
+#include "pdiagnostic.h"
diff --git a/Source/library/FBSUtil/flatcc/portable/pendian.h b/Source/library/FBSUtil/flatcc/portable/pendian.h
new file mode 100644
index 0000000..d5bdc6f
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pendian.h
@@ -0,0 +1,177 @@
+#ifndef PENDIAN_H
+#define PENDIAN_H
+
+/*
+ * Defines platform optimized (as per linux
+ *
+ * le16toh, le32to, le64toh, be16toh, be32toh, be64toh
+ * htole16, htole32, htole64, htobe16, htobe32, htobe64
+ *
+ * Falls back to auto-detect endian conversion which is also fast
+ * if fast byteswap operation was detected.
+ *
+ * Also defines platform optimized:
+ *
+ * bswap16, bswap32, bswap64,
+ *
+ * with fall-back to shift-or implementation.
+ *
+ * For convenience also defines:
+ *
+ * le8to, be8toh, htole8, htobe8
+ * bswap8
+ *
+ * The convience functions makes is simpler to define conversion macros
+ * based on type size.
+ *
+ * NOTE: this implementation expects arguments with no side-effects and
+ * with appropriately sized unsigned arguments. These are expected to be
+ * used with typesafe wrappers.
+ */
+
+#ifndef UINT8_MAX
+#include "pstdint.h"
+#endif
+
+#include "pendian_detect.h"
+
+#if defined(_MSC_VER)
+#if _MSC_VER >= 1300
+#include
+#define bswap16 _byteswap_ushort
+#define bswap32 _byteswap_ulong
+#define bswap64 _byteswap_uint64
+#endif
+#elif defined(__clang__)
+#if __has_builtin(__builtin_bswap16)
+#define bswap16 __builtin_bswap16
+#endif
+#if __has_builtin(__builtin_bswap32)
+#define bswap32 __builtin_bswap32
+#endif
+#if __has_builtin(__builtin_bswap64)
+#define bswap64 __builtin_bswap64
+#endif
+#elif defined(__OpenBSD__)
+#include
+#define bswap16 swap16
+#define bswap32 swap32
+#define bswap64 swap64
+#elif defined(__GNUC__) /* Supported since at least GCC 4.4 */
+#define bswap32 __builtin_bswap32
+#define bswap64 __builtin_bswap64
+#endif
+
+#ifndef bswap16
+#define bswap16(v) \
+ (((uint16_t)(v) << 8) | ((uint16_t)(v) >> 8))
+#endif
+
+#ifndef bswap32
+#define bswap32(v) \
+ ((((uint32_t)(v) << 24)) \
+ | (((uint32_t)(v) << 8) & UINT32_C(0x00FF0000)) \
+ | (((uint32_t)(v) >> 8) & UINT32_C(0x0000FF00)) \
+ | (((uint32_t)(v) >> 24)))
+#endif
+
+#ifndef bswap64
+#define bswap64(v) \
+ ((((uint64_t)(v) << 56)) \
+ | (((uint64_t)(v) << 40) & UINT64_C(0x00FF000000000000)) \
+ | (((uint64_t)(v) << 24) & UINT64_C(0x0000FF0000000000)) \
+ | (((uint64_t)(v) << 8) & UINT64_C(0x000000FF00000000)) \
+ | (((uint64_t)(v) >> 8) & UINT64_C(0x00000000FF000000)) \
+ | (((uint64_t)(v) >> 24) & UINT64_C(0x0000000000FF0000)) \
+ | (((uint64_t)(v) >> 40) & UINT64_C(0x000000000000FF00)) \
+ | (((uint64_t)(v) >> 56)))
+#endif
+
+#ifndef bswap8
+#define bswap8(v) ((uint8_t)(v))
+#endif
+
+#if !defined(le16toh) && defined(letoh16)
+#define le16toh letoh16
+#define le32toh letoh32
+#define le64toh letoh64
+#endif
+
+#if !defined(be16toh) && defined(betoh16)
+#define be16toh betoh16
+#define be32toh betoh32
+#define be64toh betoh64
+#endif
+
+/* Assume it goes for all. */
+#if !defined(le16toh)
+
+#if defined(__LITTLE_ENDIAN__)
+
+#define le16toh(v) (v)
+#define le32toh(v) (v)
+#define le64toh(v) (v)
+
+#define htole16(v) (v)
+#define htole32(v) (v)
+#define htole64(v) (v)
+
+#define be16toh(v) bswap16(v)
+#define be32toh(v) bswap32(v)
+#define be64toh(v) bswap64(v)
+
+#define htobe16(v) bswap16(v)
+#define htobe32(v) bswap32(v)
+#define htobe64(v) bswap64(v)
+
+#elif defined(__BIG_ENDIAN__)
+
+#define le16toh(v) bswap16(v)
+#define le32toh(v) bswap32(v)
+#define le64toh(v) bswap64(v)
+
+#define htole16(v) bswap16(v)
+#define htole32(v) bswap32(v)
+#define htole64(v) bswap64(v)
+
+#define be16toh(v) (v)
+#define be32toh(v) (v)
+#define be64toh(v) (v)
+
+#define htobe16(v) (v)
+#define htobe32(v) (v)
+#define htobe64(v) (v)
+
+#else
+
+static const int __pendian_test = 1;
+
+#define le16toh(v) (*(char *)&__pendian_test ? (v) : bswap16(v))
+#define le32toh(v) (*(char *)&__pendian_test ? (v) : bswap32(v))
+#define le64toh(v) (*(char *)&__pendian_test ? (v) : bswap64(v))
+
+#define htole16(v) (*(char *)&__pendian_test ? (v) : bswap16(v))
+#define htole32(v) (*(char *)&__pendian_test ? (v) : bswap32(v))
+#define htole64(v) (*(char *)&__pendian_test ? (v) : bswap64(v))
+
+#define be16toh(v) (*(char *)&__pendian_test ? bswap16(v) : (v))
+#define be32toh(v) (*(char *)&__pendian_test ? bswap32(v) : (v))
+#define be64toh(v) (*(char *)&__pendian_test ? bswap64(v) : (v))
+
+#define htobe16(v) (*(char *)&__pendian_test ? bswap16(v) : (v))
+#define htobe32(v) (*(char *)&__pendian_test ? bswap32(v) : (v))
+#define htobe64(v) (*(char *)&__pendian_test ? bswap64(v) : (v))
+
+#endif
+
+#endif /* le16toh */
+
+/* Helpers not part of Linux */
+#if !defined(le8toh)
+#define le8toh(n) (n)
+#define htole8(n) (n)
+#define be8toh(n) (n)
+#define htobe8(n) (n)
+#endif
+
+#endif /* PENDIAN_H */
diff --git a/Source/library/FBSUtil/flatcc/portable/pendian_detect.h b/Source/library/FBSUtil/flatcc/portable/pendian_detect.h
new file mode 100644
index 0000000..fdf304e
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pendian_detect.h
@@ -0,0 +1,110 @@
+/*
+ * Uses various known flags to decide endianness and defines:
+ *
+ * __LITTLE_ENDIAN__ or __BIG_ENDIAN__ if not already defined
+ *
+ * and also defines
+ *
+ * __BYTE_ORDER__ to either __ORDER_LITTLE_ENDIAN__ or
+ * __ORDER_BIG_ENDIAN__ if not already defined
+ *
+ * If none of these could be set, __UNKNOWN_ENDIAN__ is defined,
+ * which is not a known flag. If __BYTE_ORDER__ is defined but
+ * not big or little endian, __UNKNOWN_ENDIAN__ is also defined.
+ *
+ * Note: Some systems define __BYTE_ORDER without __ at the end -
+ * we detect this and map it to __BYTE_ORDER__.
+ */
+
+#ifndef PENDIAN_DETECT
+#define PENDIAN_DETECT
+
+#ifndef __ORDER_LITTLE_ENDIAN__
+#define __ORDER_LITTLE_ENDIAN__ 1234
+#endif
+
+#ifndef __ORDER_BIG_ENDIAN__
+#define __ORDER_BIG_ENDIAN__ 4321
+#endif
+
+#ifdef __BYTE_ORDER__
+
+#if defined(__LITTLE_ENDIAN__) && __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
+#error __LITTLE_ENDIAN__ inconsistent with __BYTE_ORDER__
+#endif
+
+#if defined(__BIG_ENDIAN__) && __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__
+#error __BIG_ENDIAN__ inconsistent with __BYTE_ORDER__
+#endif
+
+#else /* __BYTE_ORDER__ */
+
+
+#if \
+ defined(__LITTLE_ENDIAN__) || \
+ (defined(__BYTE_ORDER) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \
+ defined(__ARMEL__) || defined(__THUMBEL__) || \
+ defined(__AARCH64EL__) || \
+ (defined(_MSC_VER) && defined(_M_ARM)) || \
+ defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || \
+ defined(_M_X64) || defined(_M_IX86) || defined(_M_I86) || \
+ defined(__i386__) || defined(__alpha__) || \
+ defined(__ia64) || defined(__ia64__) || \
+ defined(_M_IA64) || defined(_M_ALPHA) || \
+ defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || \
+ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
+ defined(__bfin__)
+
+#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
+
+#endif
+
+#if \
+ defined (__BIG_ENDIAN__) || \
+ (defined(__BYTE_ORDER) && __BYTE_ORDER == __ORDER_BIG_ENDIAN) || \
+ defined(__ARMEB__) || defined(THUMBEB__) || defined (__AARCH64EB__) || \
+ defined(_MIPSEB) || defined(__MIPSEB) || defined(__MIPSEB__) || \
+ defined(__sparc) || defined(__sparc__) || \
+ defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || \
+ defined(__hpux) || defined(__hppa) || defined(__s390__)
+
+#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
+
+#endif
+
+#endif /* __BYTE_ORDER__ */
+
+#ifdef __BYTE_ORDER__
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+
+#ifndef __LITTLE_ENDIAN__
+#define __LITTLE_ENDIAN__ 1
+#endif
+
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+
+#ifndef __BIG_ENDIAN__
+#define __BIG_ENDIAN__ 1
+#endif
+
+#else
+
+/*
+ * Custom extension - we only define __BYTE_ORDER if known big or little.
+ * User code that understands __BYTE_ORDER__ may also assume unkown if
+ * it is not defined by know - this will allow other endian formats than
+ * big or little when supported by compiler.
+ */
+#ifndef __UNKNOWN_ENDIAN__
+#define __UNKNOWN_ENDIAN__ 1
+#endif
+
+#endif
+#endif /* __BYTE_ORDER__ */
+
+#if defined(__LITTLE_ENDIAN__) && defined(__BIG_ENDIAN__)
+#error conflicting definitions of __LITTLE_ENDIAN__ and __BIG_ENDIAN__
+#endif
+
+#endif /* PENDIAN_DETECT */
diff --git a/Source/library/FBSUtil/flatcc/portable/pinline.h b/Source/library/FBSUtil/flatcc/portable/pinline.h
new file mode 100644
index 0000000..f4f8f27
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pinline.h
@@ -0,0 +1,19 @@
+#ifndef PINLINE_H
+#define PINLINE_H
+
+#ifndef __cplusplus
+
+#if (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
+/* C99 or newer */
+#elif _MSC_VER >= 1500 /* MSVC 9 or newer */
+#undef inline
+#define inline __inline
+#elif __GNUC__ >= 3 /* GCC 3 or newer */
+#define inline __inline
+#else /* Unknown or ancient */
+#define inline
+#endif
+
+#endif /* __cplusplus */
+
+#endif /* PINLINE_H */
diff --git a/Source/library/FBSUtil/flatcc/portable/pinttypes.h b/Source/library/FBSUtil/flatcc/portable/pinttypes.h
new file mode 100644
index 0000000..5833cec
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pinttypes.h
@@ -0,0 +1,69 @@
+#ifndef PINTTYPES_H
+#define PINTTYPES_H
+
+#if (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
+/* C99 or newer */
+#include
+#else
+
+/*
+ * This is not a complete implementation of , just the most
+ * useful printf modifiers.
+ */
+
+#include "pstdint.h"
+
+#ifndef PRINTF_INT64_MODIFIER
+#error "please define PRINTF_INT64_MODIFIER"
+#endif
+
+#ifndef PRId64
+#define PRId64 PRINTF_INT64_MODIFIER "d"
+#define PRIu64 PRINTF_INT64_MODIFIER "u"
+#define PRIx64 PRINTF_INT64_MODIFIER "x"
+#endif
+
+#ifndef PRINTF_INT32_MODIFIER
+#define PRINTF_INT32_MODIFIER "l"
+#endif
+
+#ifndef PRId32
+#define PRId32 PRINTF_INT32_MODIFIER "d"
+#define PRIu32 PRINTF_INT32_MODIFIER "u"
+#define PRIx32 PRINTF_INT32_MODIFIER "x"
+#endif
+
+#ifndef PRINTF_INT16_MODIFIER
+#define PRINTF_INT16_MODIFIER "h"
+#endif
+
+#ifndef PRId16
+#define PRId16 PRINTF_INT16_MODIFIER "d"
+#define PRIu16 PRINTF_INT16_MODIFIER "u"
+#define PRIx16 PRINTF_INT16_MODIFIER "x"
+#endif
+
+# endif /* __STDC__ */
+
+#ifdef PORTABLE_USE_DEPRECATED_INTTYPES
+#ifndef PRIszu
+#ifdef _MSC_VER
+ #define PRIszd "Id"
+ #define PRIszu "Iu"
+ #define PRIszx "Ix"
+ #define PRIpdu "Iu"
+ #define PRIpdd "Id"
+ #define PRIpdx "Ix"
+#else
+ #define PRIszd "zd"
+ #define PRIszu "zu"
+ #define PRIszx "zx"
+ #define PRIpdd "td"
+ #define PRIpdu "tu"
+ #define PRIpdx "tx"
+#endif
+#endif
+#endif
+
+
+#endif /* PINTTYPES */
diff --git a/Source/library/FBSUtil/flatcc/portable/portable.h b/Source/library/FBSUtil/flatcc/portable/portable.h
new file mode 100644
index 0000000..e8f79cc
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/portable.h
@@ -0,0 +1,17 @@
+#ifndef PORTABLE_H
+#define PORTABLE_H
+
+#include "pversion.h"
+#include "pwarnings.h"
+
+/* Featutures that ought to be supported by C11, but some aren't. */
+#include "pinttypes.h"
+#include "pstdalign.h"
+#include "pinline.h"
+#include "pstatic_assert.h"
+
+/* These are not supported by C11 and are general platform abstractions. */
+#include "pendian.h"
+#include "punaligned.h"
+
+#endif /* PORTABLE_H */
diff --git a/Source/library/FBSUtil/flatcc/portable/pparsefp.h b/Source/library/FBSUtil/flatcc/portable/pparsefp.h
new file mode 100644
index 0000000..d4a3da9
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pparsefp.h
@@ -0,0 +1,113 @@
+#ifndef PPARSEFP_H
+#define PPARSEFP_H
+
+/*
+ * Parses a float or double number and returns the length parsed if
+ * successful. The length argument is of limited value due to dependency
+ * on `strtod` - buf[len] must be accessible and must not be part of
+ * a valid number, including hex float numbers..
+ *
+ * Unlike strtod, whitespace is not parsed.
+ *
+ * May return:
+ * - null on error,
+ * - buffer start if first character does not start a number,
+ * - or end of parse on success.
+ *
+ */
+
+#define PDIAGNOSTIC_IGNORE_UNUSED_FUNCTION
+#include "pdiagnostic_push.h"
+
+/*
+ * isinf is needed in order to stay compatible with strtod's
+ * over/underflow handling but isinf has some portability issues.
+ *
+ * Use the parse_double/float_is_range_error instead of isinf directly.
+ * This ensures optimizations can be added when not using strtod.
+ *
+ * On gcc, clang and msvc we can use isinf or equivalent directly.
+ * Other compilers such as xlc may require linking with -lm which may not
+ * be convienent so a default isinf is provided. If isinf is available
+ * and there is a noticable performance issue, define
+ * `PORTABLE_USE_ISINF`.
+ */
+#if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) || defined(PORTABLE_USE_ISINF)
+#include
+#if defined(_MSC_VER) && !defined(isinf)
+#include
+#define isnan _isnan
+#define isinf(x) (!_finite(x))
+#endif
+#define parse_double_isinf isinf
+#define parse_float_isinf isinf
+#else
+
+#ifndef UINT8_MAX
+#include
+#endif
+
+/* Avoid linking with libmath but depends on float/double being IEEE754 */
+static inline int parse_double_isinf(double x)
+{
+ union { uint64_t u64; double f64; } v;
+ v.f64 = x;
+ return (v.u64 & 0x7fffffff00000000ULL) == 0x7ff0000000000000ULL;
+}
+
+static inline int parse_float_isinf(float x)
+{
+ union { uint32_t u32; float f32; } v;
+ v.f32 = x;
+ return (v.u32 & 0x7fffffff) == 0x7f800000;
+}
+#endif
+
+/* Returns 0 when in range, 1 on overflow, and -1 on underflow. */
+static inline int parse_double_is_range_error(double x)
+{
+ return parse_double_isinf(x) ? (x < 0.0 ? -1 : 1) : 0;
+}
+
+static inline int parse_float_is_range_error(float x)
+{
+ return parse_float_isinf(x) ? (x < 0.0f ? -1 : 1) : 0;
+}
+
+#ifndef PORTABLE_USE_GRISU3
+#define PORTABLE_USE_GRISU3 1
+#endif
+
+#if PORTABLE_USE_GRISU3
+#include "grisu3_parse.h"
+#endif
+
+#ifdef grisu3_parse_double_is_defined
+static inline const char *parse_double(const char *buf, int len, double *result)
+{
+ return grisu3_parse_double(buf, len, result);
+}
+#else
+#include
+static inline const char *parse_double(const char *buf, int len, double *result)
+{
+ char *end;
+
+ (void)len;
+ *result = strtod(buf, &end);
+ return end;
+}
+#endif
+
+static inline const char *parse_float(const char *buf, int len, float *result)
+{
+ const char *end;
+ double v;
+
+ end = parse_double(buf, len, &v);
+ *result = (float)v;
+ return end;
+}
+
+#include "pdiagnostic_pop.h"
+#endif /* PPARSEFP_H */
diff --git a/Source/library/FBSUtil/flatcc/portable/pparseint.h b/Source/library/FBSUtil/flatcc/portable/pparseint.h
new file mode 100644
index 0000000..6796c90
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pparseint.h
@@ -0,0 +1,366 @@
+#ifndef PPARSEINT_H
+#define PPARSEINT_H
+
+/*
+ * Type specific integer parsers:
+ *
+ * const char *
+ * parse_(const char *buf, int len, *value, int *status);
+ *
+ * parse_uint64, parse_int64
+ * parse_uint32, parse_int32
+ * parse_uint16, parse_int16
+ * parse_uint8, parse_int8
+ * parse_ushort, parse_short
+ * parse_uint, parse_int
+ * parse_ulong, parse_long
+ *
+ * Leading space must be stripped in advance. Status argument can be
+ * null.
+ *
+ * Returns pointer to end of match and a non-negative status code
+ * on succcess (0 for unsigned, 1 for signed):
+ *
+ * PARSE_INTEGER_UNSIGNED
+ * PARSE_INTEGER_SIGNED
+ *
+ * Returns null with a negative status code and unmodified value on
+ * invalid integer formats:
+ *
+ * PARSE_INTEGER_OVERFLOW
+ * PARSE_INTEGER_UNDERFLOW
+ * PARSE_INTEGER_INVALID
+ *
+ * Returns input buffer with negative status code and unmodified value
+ * if first character does not start an integer (not a sign or a digit).
+ *
+ * PARSE_INTEGER_UNMATCHED
+ * PARSE_INTEGER_END
+ *
+ * The signed parsers only works with two's complement architectures.
+ *
+ * Note: the corresponding parse_float and parse_double parsers do not
+ * have a status argument because +/-Inf and NaN are conventionally used
+ * for this.
+ */
+
+#include "limits.h"
+#ifndef UINT8_MAX
+#include
+#endif
+
+#define PARSE_INTEGER_UNSIGNED 0
+#define PARSE_INTEGER_SIGNED 1
+#define PARSE_INTEGER_OVERFLOW -1
+#define PARSE_INTEGER_UNDERFLOW -2
+#define PARSE_INTEGER_INVALID -3
+#define PARSE_INTEGER_UNMATCHED -4
+#define PARSE_INTEGER_END -5
+
+/*
+ * Generic integer parser that holds 64-bit unsigned values and stores
+ * sign separately. Leading space is not valid.
+ *
+ * Note: this function differs from the type specific parsers like
+ * parse_int64 by not negating the value when there is a sign. It
+ * differs from parse_uint64 by being able to return a negative
+ * UINT64_MAX successfully.
+ *
+ * This parser is used by all type specific integer parsers.
+ *
+ * Status argument can be null.
+ */
+static const char *parse_integer(const char *buf, int len, uint64_t *value, int *status)
+{
+ uint64_t x0, x = 0;
+ const char *k, *end = buf + len;
+ int sign, status_;
+
+ if (!status) {
+ status = &status_;
+ }
+ if (buf == end) {
+ *status = PARSE_INTEGER_END;
+ return buf;
+ }
+ k = buf;
+ sign = *buf == '-';
+ buf += sign;
+ while (buf != end && *buf >= '0' && *buf <= '9') {
+ x0 = x;
+ x = x * 10 + *buf - '0';
+ if (x0 > x) {
+ *status = sign ? PARSE_INTEGER_UNDERFLOW : PARSE_INTEGER_OVERFLOW;
+ return 0;
+ }
+ ++buf;
+ }
+ if (buf == k) {
+ /* No number was matched, but it isn't an invalid number either. */
+ *status = PARSE_INTEGER_UNMATCHED;
+ return buf;
+ }
+ if (buf == k + sign) {
+ *status = PARSE_INTEGER_INVALID;
+ return 0;
+ }
+ if (buf != end)
+ switch (*buf) {
+ case 'e': case 'E': case '.': case 'p': case 'P':
+ *status = PARSE_INTEGER_INVALID;
+ return 0;
+ }
+ *value = x;
+ *status = sign;
+ return buf;
+}
+
+/*
+ * Parse hex values like 0xff, -0xff, 0XdeAdBeaf42, cannot be trailed by '.', 'p', or 'P'.
+ * Overflows if string is more than 16 valid hex digits. Otherwise similar to parse_integer.
+ */
+static const char *parse_hex_integer(const char *buf, int len, uint64_t *value, int *status)
+{
+ uint64_t x = 0;
+ const char *k, *k2, *end = buf + len;
+ int sign, status_;
+ unsigned char c;
+
+ if (!status) {
+ status = &status_;
+ }
+ if (buf == end) {
+ *status = PARSE_INTEGER_END;
+ return buf;
+ }
+ sign = *buf == '-';
+ buf += sign;
+ if (end - buf < 2 || buf[0] != '0' || (buf[1] | 0x20) != 'x') {
+ *status = PARSE_INTEGER_UNMATCHED;
+ return buf - sign;
+ }
+ buf += 2;
+ k = buf;
+ k2 = end;
+ if (end - buf > 16) {
+ k2 = buf + 16;
+ }
+ while (buf != k2) {
+ c = *buf;
+ if (c >= '0' && c <= '9') {
+ x = x * 16 + c - '0';
+ } else {
+ /* Lower case. */
+ c |= 0x20;
+ if (c >= 'a' && c <= 'f') {
+ x = x * 16 + c - 'a' + 10;
+ } else {
+ break;
+ }
+ }
+ ++buf;
+ }
+ if (buf == k) {
+ if (sign) {
+ *status = PARSE_INTEGER_INVALID;
+ return 0;
+ } else {
+ /* No number was matched, but it isn't an invalid number either. */
+ *status = PARSE_INTEGER_UNMATCHED;
+ return buf;
+ }
+ }
+ if (buf == end) {
+ goto done;
+ }
+ c = *buf;
+ if (buf == k2) {
+ if (c >= '0' && c <= '9') {
+ *status = sign ? PARSE_INTEGER_UNDERFLOW : PARSE_INTEGER_OVERFLOW;
+ return 0;
+ }
+ c |= 0x20;
+ if (c >= 'a' && c <= 'f') {
+ *status = sign ? PARSE_INTEGER_UNDERFLOW : PARSE_INTEGER_OVERFLOW;
+ return 0;
+ }
+ }
+ switch (c) {
+ case '.': case 'p': case 'P':
+ *status = PARSE_INTEGER_INVALID;
+ return 0;
+ }
+done:
+ *value = x;
+ *status = sign;
+ return buf;
+}
+
+
+#define __portable_define_parse_unsigned(NAME, TYPE, LIMIT) \
+static inline const char *parse_ ## NAME \
+ (const char *buf, int len, TYPE *value, int *status) \
+{ \
+ int status_ = 0; \
+ uint64_t x; \
+ \
+ if (!status) { \
+ status = &status_; \
+ } \
+ buf = parse_integer(buf, len, &x, status); \
+ switch (*status) { \
+ case PARSE_INTEGER_UNSIGNED: \
+ if (x <= LIMIT) { \
+ *value = (TYPE)x; \
+ return buf; \
+ } \
+ *status = PARSE_INTEGER_OVERFLOW; \
+ return 0; \
+ case PARSE_INTEGER_SIGNED: \
+ *status = PARSE_INTEGER_UNDERFLOW; \
+ return 0; \
+ default: \
+ return buf; \
+ } \
+}
+
+#define __portable_define_parse_hex_unsigned(NAME, TYPE, LIMIT) \
+static inline const char *parse_hex_ ## NAME \
+ (const char *buf, int len, TYPE *value, int *status) \
+{ \
+ int status_ = 0; \
+ uint64_t x; \
+ \
+ if (!status) { \
+ status = &status_; \
+ } \
+ buf = parse_hex_integer(buf, len, &x, status); \
+ switch (*status) { \
+ case PARSE_INTEGER_UNSIGNED: \
+ if (x <= LIMIT) { \
+ *value = (TYPE)x; \
+ return buf; \
+ } \
+ *status = PARSE_INTEGER_OVERFLOW; \
+ return 0; \
+ case PARSE_INTEGER_SIGNED: \
+ *status = PARSE_INTEGER_UNDERFLOW; \
+ return 0; \
+ default: \
+ return buf; \
+ } \
+}
+
+/* This assumes two's complement. */
+#define __portable_define_parse_signed(NAME, TYPE, LIMIT) \
+static inline const char *parse_ ## NAME \
+ (const char *buf, int len, TYPE *value, int *status) \
+{ \
+ int status_ = 0; \
+ uint64_t x; \
+ \
+ if (!status) { \
+ status = &status_; \
+ } \
+ buf = parse_integer(buf, len, &x, status); \
+ switch (*status) { \
+ case PARSE_INTEGER_UNSIGNED: \
+ if (x <= LIMIT) { \
+ *value = (TYPE)x; \
+ return buf; \
+ } \
+ *status = PARSE_INTEGER_OVERFLOW; \
+ return 0; \
+ case PARSE_INTEGER_SIGNED: \
+ if (x <= (uint64_t)(LIMIT) + 1) { \
+ *value = (TYPE)-(int64_t)x; \
+ return buf; \
+ } \
+ *status = PARSE_INTEGER_UNDERFLOW; \
+ return 0; \
+ default: \
+ return buf; \
+ } \
+}
+
+/* This assumes two's complement. */
+#define __portable_define_parse_hex_signed(NAME, TYPE, LIMIT) \
+static inline const char *parse_hex_ ## NAME \
+ (const char *buf, int len, TYPE *value, int *status) \
+{ \
+ int status_ = 0; \
+ uint64_t x; \
+ \
+ if (!status) { \
+ status = &status_; \
+ } \
+ buf = parse_hex_integer(buf, len, &x, status); \
+ switch (*status) { \
+ case PARSE_INTEGER_UNSIGNED: \
+ if (x <= LIMIT) { \
+ *value = (TYPE)x; \
+ return buf; \
+ } \
+ *status = PARSE_INTEGER_OVERFLOW; \
+ return 0; \
+ case PARSE_INTEGER_SIGNED: \
+ if (x <= (uint64_t)(LIMIT) + 1) { \
+ *value = (TYPE)-(int64_t)x; \
+ return buf; \
+ } \
+ *status = PARSE_INTEGER_UNDERFLOW; \
+ return 0; \
+ default: \
+ return buf; \
+ } \
+}
+
+static inline const char *parse_uint64(const char *buf, int len, uint64_t *value, int *status)
+{
+ buf = parse_integer(buf, len, value, status);
+ if (*status == PARSE_INTEGER_SIGNED) {
+ *status = PARSE_INTEGER_UNDERFLOW;
+ return 0;
+ }
+ return buf;
+}
+
+static inline const char *parse_hex_uint64(const char *buf, int len, uint64_t *value, int *status)
+{
+ buf = parse_hex_integer(buf, len, value, status);
+ if (*status == PARSE_INTEGER_SIGNED) {
+ *status = PARSE_INTEGER_UNDERFLOW;
+ return 0;
+ }
+ return buf;
+}
+
+__portable_define_parse_signed(int64, int64_t, INT64_MAX)
+__portable_define_parse_signed(int32, int32_t, INT32_MAX)
+__portable_define_parse_unsigned(uint16, uint16_t, UINT16_MAX)
+__portable_define_parse_signed(int16, int16_t, INT16_MAX)
+__portable_define_parse_unsigned(uint8, uint8_t, UINT8_MAX)
+__portable_define_parse_signed(int8, int8_t, INT8_MAX)
+
+__portable_define_parse_hex_signed(int64, int64_t, INT64_MAX)
+__portable_define_parse_hex_signed(int32, int32_t, INT32_MAX)
+__portable_define_parse_hex_unsigned(uint16, uint16_t, UINT16_MAX)
+__portable_define_parse_hex_signed(int16, int16_t, INT16_MAX)
+__portable_define_parse_hex_unsigned(uint8, uint8_t, UINT8_MAX)
+__portable_define_parse_hex_signed(int8, int8_t, INT8_MAX)
+
+__portable_define_parse_unsigned(ushort, unsigned short, USHRT_MAX)
+__portable_define_parse_signed(short, short, SHRT_MAX)
+__portable_define_parse_unsigned(uint, unsigned int, UINT_MAX)
+__portable_define_parse_signed(int, int, INT_MAX)
+__portable_define_parse_unsigned(ulong, unsigned long, ULONG_MAX)
+__portable_define_parse_signed(long, unsigned long, LONG_MAX)
+
+__portable_define_parse_hex_unsigned(ushort, unsigned short, USHRT_MAX)
+__portable_define_parse_hex_signed(short, short, SHRT_MAX)
+__portable_define_parse_hex_unsigned(uint, unsigned int, UINT_MAX)
+__portable_define_parse_hex_signed(int, int, INT_MAX)
+__portable_define_parse_hex_unsigned(ulong, unsigned long, ULONG_MAX)
+__portable_define_parse_hex_signed(long, unsigned long, LONG_MAX)
+
+#endif /* PPARSEINT_H */
diff --git a/Source/library/FBSUtil/flatcc/portable/pprintfp.h b/Source/library/FBSUtil/flatcc/portable/pprintfp.h
new file mode 100644
index 0000000..76e0cbe
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pprintfp.h
@@ -0,0 +1,30 @@
+#ifndef PPRINTFP_H
+#define PPRINTFP_H
+
+#define PDIAGNOSTIC_IGNORE_UNUSED_FUNCTION
+#include "pdiagnostic_push.h"
+
+#ifndef PORTABLE_USE_GRISU3
+#define PORTABLE_USE_GRISU3 1
+#endif
+
+
+#if PORTABLE_USE_GRISU3
+#include "grisu3_print.h"
+#endif
+
+#ifdef grisu3_print_double_is_defined
+/* Currently there is not special support for floats. */
+#define print_float(n, p) grisu3_print_double((float)(n), (p))
+#define print_double(n, p) grisu3_print_double((double)(n), (p))
+#else
+#include
+#define print_float(n, p) sprintf(p, "%.9g", (float)(n))
+#define print_double(n, p) sprintf(p, "%.17g", (double)(n))
+#endif
+
+#define print_hex_float(n, p) sprintf(p, "%a", (float)(n))
+#define print_hex_double(n, p) sprintf(p, "%a", (double)(n))
+
+#include "pdiagnostic_pop.h"
+#endif /* PPRINTFP_H */
diff --git a/Source/library/FBSUtil/flatcc/portable/pprintint.h b/Source/library/FBSUtil/flatcc/portable/pprintint.h
new file mode 100644
index 0000000..38e6d0c
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pprintint.h
@@ -0,0 +1,598 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Mikkel F. Jørgensen, dvide.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ *
+ * Fast printing of (u)int8/16/32/64_t, (u)int, (u)long.
+ *
+ * Functions take for the
+ *
+ * int print_(type value, char *buf);
+ *
+ * and returns number of characters printed, excluding trailing '\0'
+ * which is also printed. Prints at most 21 characters including zero-
+ * termination.
+ *
+ * The function `print_bool` is a bit different - it simply prints "true\0" for
+ * non-zero integers, and "false\0" otherwise.
+ *
+ * The general algorithm is in-place formatting using binary search log10
+ * followed by duff device loop unrolling div / 100 stages.
+ *
+ * The simpler post copy algorithm also provided for fmt_(u)int uses a
+ * temp buffer and loops over div/100 and post copy to target buffer.
+ *
+ *
+ * Benchmarks on core-i7, 2.2GHz, 64-bit clang/OS-X -O2:
+ *
+ * print_int64: avg 15ns for values between INT64_MIN + (10^7/2 .. 10^7/2)
+ * print_int64: avg 11ns for values between 10^9 + (0..10,000,000).
+ * print_int32: avg 7ns for values cast from INT64_MIN + (10^7/2 .. 10^7/2)
+ * print_int32: avg 7ns for values between 10^9 + (0..10,000,000).
+ * print_int64: avg 13ns for values between 10^16 + (0..10,000,000).
+ * print_int64: avg 5ns for values between 0 and 10,000,000.
+ * print_int32: avg 5ns for values between 0 and 10,000,000.
+ * print_int16: avg 10ns for values cast from 0 and 10,000,000.
+ * print_int8: avg 4ns for values cast from 0 and 10,000,000.
+ *
+ * Post copy algorithm:
+ * print_int: avg 12ns for values between INT64_MIN + (10^7/2 .. 10^7/2)
+ * print_int: avg 14ns for values between 10^9 + (0..10,000,000).
+ * print_long: avg 29ns for values between INT64_MIN + (10^7/2 .. 10^7/2)
+ *
+ * The post copy algorithm is nearly half as fast as the in-place
+ * algorithm, but can also be faster occasionally - possibly because the
+ * optimizer being able to skip the copy step.
+ */
+
+#ifndef PPRINTINT_H
+#define PPRINTINT_H
+
+#ifndef UINT8_MAX
+#include
+#endif
+
+#define PDIAGNOSTIC_IGNORE_UNUSED_FUNCTION
+#include "pdiagnostic_push.h"
+
+static int print_bool(int n, char *p);
+
+static int print_uint8(uint8_t n, char *p);
+static int print_uint16(uint16_t n, char *p);
+static int print_uint32(uint32_t n, char *p);
+static int print_uint64(uint64_t n, char *p);
+static int print_int8(int8_t n, char *p);
+static int print_int16(int16_t n, char *p);
+static int print_int32(int32_t n, char *p);
+static int print_int64(int64_t n, char *p);
+
+/*
+ * Uses slightly slower, but more compact alogrithm
+ * that is not hardcoded to implementation size.
+ * Other types may be defined using macros below.
+ */
+static int print_ulong(unsigned long n, char *p);
+static int print_uint(unsigned int n, char *p);
+static int print_int(int n, char *p);
+static int print_long(long n, char *p);
+
+
+#if defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64)
+#define __print_unaligned_copy_16(p, q) (*(uint16_t*)(p) = *(uint16_t*)(q))
+#else
+#define __print_unaligned_copy_16(p, q) \
+ ((((uint8_t*)(p))[0] = ((uint8_t*)(q))[0]), \
+ (((uint8_t*)(p))[1] = ((uint8_t*)(q))[1]))
+#endif
+
+static const char __print_digit_pairs[] =
+ "0001020304050607080910111213141516171819"
+ "2021222324252627282930313233343536373839"
+ "4041424344454647484950515253545556575859"
+ "6061626364656667686970717273747576777879"
+ "8081828384858687888990919293949596979899";
+
+#define __print_stage() \
+ p -= 2; \
+ dp = __print_digit_pairs + (n % 100) * 2; \
+ n /= 100; \
+ __print_unaligned_copy_16(p, dp);
+
+#define __print_long_stage() \
+ __print_stage() \
+ __print_stage()
+
+#define __print_short_stage() \
+ *--p = (n % 10) + '0'; \
+ n /= 10;
+
+static int print_bool(int n, char *buf)
+{
+ if (n) {
+ memcpy(buf, "true\0", 5);
+ return 4;
+ } else {
+ memcpy(buf, "false\0", 6);
+ return 5;
+ }
+}
+
+static int print_uint8(uint8_t n, char *p)
+{
+ const char *dp;
+
+ if (n >= 100) {
+ p += 3;
+ *p = '\0';
+ __print_stage();
+ p[-1] = n + '0';
+ return 3;
+ }
+ if (n >= 10) {
+ p += 2;
+ *p = '\0';
+ __print_stage();
+ return 2;
+ }
+ p[1] = '\0';
+ p[0] = n + '0';
+ return 1;
+}
+
+static int print_uint16(uint16_t n, char *p)
+{
+ int k = 0;
+ const char *dp;
+
+ if (n >= 1000) {
+ if(n >= 10000) {
+ k = 5;
+ } else {
+ k = 4;
+ }
+ } else {
+ if(n >= 100) {
+ k = 3;
+ } else if(n >= 10) {
+ k = 2;
+ } else {
+ k = 1;
+ }
+ }
+ p += k;
+ *p = '\0';
+ if (k & 1) {
+ switch (k) {
+ case 5:
+ __print_stage();
+ case 3:
+ __print_stage();
+ case 1:
+ p[-1] = n + '0';
+ }
+ } else {
+ switch (k) {
+ case 4:
+ __print_stage();
+ case 2:
+ __print_stage();
+ }
+ }
+ return k;
+}
+
+static int print_uint32(uint32_t n, char *p)
+{
+ int k = 0;
+ const char *dp;
+
+ if(n >= 10000UL) {
+ if(n >= 10000000UL) {
+ if(n >= 1000000000UL) {
+ k = 10;
+ } else if(n >= 100000000UL) {
+ k = 9;
+ } else {
+ k = 8;
+ }
+ } else {
+ if(n >= 1000000UL) {
+ k = 7;
+ } else if(n >= 100000UL) {
+ k = 6;
+ } else {
+ k = 5;
+ }
+ }
+ } else {
+ if(n >= 100UL) {
+ if(n >= 1000UL) {
+ k = 4;
+ } else {
+ k = 3;
+ }
+ } else {
+ if(n >= 10UL) {
+ k = 2;
+ } else {
+ k = 1UL;
+ }
+ }
+ }
+ p += k;
+ *p = '\0';
+ if (k & 1) {
+ switch (k) {
+ case 9:
+ __print_stage();
+ case 7:
+ __print_stage();
+ case 5:
+ __print_stage();
+ case 3:
+ __print_stage();
+ case 1:
+ p[-1] = n + '0';
+ }
+ } else {
+ switch (k) {
+ case 10:
+ __print_stage();
+ case 8:
+ __print_stage();
+ case 6:
+ __print_stage();
+ case 4:
+ __print_stage();
+ case 2:
+ __print_stage();
+ }
+ }
+ return k;
+}
+
+static int print_uint64(uint64_t n, char *p)
+{
+ int k = 0;
+ const char *dp;
+ const uint64_t x = 1000000000ULL;
+
+ if (n < x) {
+ return print_uint32((uint32_t)n, p);
+ }
+ if(n >= 10000ULL * x) {
+ if(n >= 10000000ULL * x) {
+ if(n >= 1000000000ULL * x) {
+ if (n >= 10000000000ULL * x) {
+ k = 11 + 9;
+ } else {
+ k = 10 + 9;
+ }
+ } else if(n >= 100000000ULL * x) {
+ k = 9 + 9;
+ } else {
+ k = 8 + 9;
+ }
+ } else {
+ if(n >= 1000000ULL * x) {
+ k = 7 + 9;
+ } else if(n >= 100000ULL * x) {
+ k = 6 + 9;
+ } else {
+ k = 5 + 9;
+ }
+ }
+ } else {
+ if(n >= 100ULL * x) {
+ if(n >= 1000ULL * x) {
+ k = 4 + 9;
+ } else {
+ k = 3 + 9;
+ }
+ } else {
+ if(n >= 10ULL * x) {
+ k = 2 + 9;
+ } else {
+ k = 1 + 9;
+ }
+ }
+ }
+ p += k;
+ *p = '\0';
+ if (k & 1) {
+ switch (k) {
+ case 19:
+ __print_stage();
+ case 17:
+ __print_stage();
+ case 15:
+ __print_stage();
+ case 13:
+ __print_stage();
+ case 11:
+ __print_stage()
+ __print_short_stage();
+ }
+ } else {
+ switch (k) {
+ case 20:
+ __print_stage();
+ case 18:
+ __print_stage();
+ case 16:
+ __print_stage();
+ case 14:
+ __print_stage();
+ case 12:
+ __print_stage();
+ case 10:
+ __print_stage();
+ }
+ }
+ __print_long_stage()
+ __print_long_stage()
+ return k;
+}
+
+static int print_int8(int8_t n, char *p)
+{
+ int sign;
+
+ if ((sign = n < 0)) {
+ *p++ = '-';
+ n = -n;
+ }
+ return print_uint8(n, p) + sign;
+}
+
+static int print_int16(int16_t n, char *p)
+{
+ int sign;
+
+ if ((sign = n < 0)) {
+ *p++ = '-';
+ n = -n;
+ }
+ return print_uint16(n, p) + sign;
+}
+
+static int print_int32(int32_t n, char *p)
+{
+ int sign;
+
+ if ((sign = n < 0)) {
+ *p++ = '-';
+ n = -n;
+ }
+ return print_uint32(n, p) + sign;
+}
+
+static int print_int64(int64_t n, char *p)
+{
+ int sign;
+
+ if ((sign = n < 0)) {
+ *p++ = '-';
+ n = -n;
+ }
+ return print_uint64(n, p) + sign;
+}
+
+#define __define_print_int_simple(NAME, UNAME, T, UT) \
+static int UNAME(UT n, char *buf) \
+{ \
+ char tmp[20]; \
+ char* p = tmp + 20; \
+ char* q = p; \
+ unsigned int k, m; \
+ \
+ while (n >= 100) { \
+ p -= 2; \
+ m = (unsigned int)(n % 100) * 2; \
+ n /= 100; \
+ __print_unaligned_copy_16(p, __print_digit_pairs + m); \
+ } \
+ p -= 2; \
+ m = (unsigned int)n * 2; \
+ __print_unaligned_copy_16(p, __print_digit_pairs + m); \
+ if (n < 10) { \
+ ++p; \
+ } \
+ k = (unsigned int)(q - p); \
+ while (p != q) { \
+ *buf++ = *p++; \
+ } \
+ *buf = '\0'; \
+ return k; \
+} \
+ \
+static int NAME(T n, char *buf) \
+{ \
+ int sign = n < 0; \
+ \
+ if (sign) { \
+ *buf++ = '-'; \
+ n = -n; \
+ } \
+ return UNAME((UT)n, buf) + sign; \
+}
+
+__define_print_int_simple(print_int, print_uint, int, unsigned int)
+__define_print_int_simple(print_long, print_ulong, long, unsigned long)
+
+#ifdef PPRINTINT_BENCH
+int main() {
+ int64_t count = 10000000; /* 10^7 */
+#if 0
+ int64_t base = 0;
+ int64_t base = 10000000000000000; /* 10^16 */
+ int64_t base = 1000000000; /* 10^9 */
+#endif
+ int64_t base = INT64_MIN - count/2;
+ char buf[100];
+ int i, k = 0, n = 0;
+ for (i = 0; i < count; i++) {
+ k = print_int64(i + base, buf);
+ n += buf[0] + buf[k - 1];
+ }
+ return n;
+}
+/* Call with time on executable, multiply time in seconds by 100 to get time unit in ns/number. */
+#endif /* PPRINTINT_BENCH */
+
+#ifdef PPRINTINT_TEST
+
+#include
+#include
+
+int main()
+{
+ char buf[21];
+ int failed = 0;
+ int k;
+
+ k = print_uint64(UINT64_MAX, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("18446744073709551615", buf)) {
+ printf("UINT64_MAX didn't print correctly, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_int64(INT64_MAX, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("9223372036854775807", buf)) {
+ printf("INT64_MAX didn't print correctly, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_int64(INT64_MIN, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("-9223372036854775808", buf)) {
+ printf("INT64_MIN didn't print correctly, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_uint32(UINT32_MAX, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("4294967295", buf)) {
+ printf("UINT32_MAX didn't print correctly, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_int32(INT32_MAX, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("2147483647", buf)) {
+ printf("INT32_MAX didn't print correctly, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_int32(INT32_MIN, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("-2147483648", buf)) {
+ printf("INT32_MIN didn't print correctly, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_uint16(UINT16_MAX, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("65535", buf)) {
+ printf("UINT16_MAX didn't print correctly, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_int16(INT16_MAX, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("32767", buf)) {
+ printf("INT16_MAX didn't print correctly, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_int16(INT16_MIN, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("-32768", buf)) {
+ printf("INT16_MIN didn't print correctly, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_uint8(UINT8_MAX, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("255", buf)) {
+ printf("INT8_MAX didn't print correctly, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_int8(INT8_MAX, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("127", buf)) {
+ printf("INT8_MAX didn't print correctly, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_int8(INT8_MIN, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("-128", buf)) {
+ printf("INT8_MIN didn't print correctly, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_int(INT32_MAX, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("2147483647", buf)) {
+ printf("INT32_MAX didn't print correctly with k = print_int, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_int(INT32_MIN, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("-2147483648", buf)) {
+ printf("INT32_MIN didn't print correctly k = print_int, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_long(INT32_MAX, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("2147483647", buf)) {
+ printf("INT32_MAX didn't print correctly with fmt_long, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_long(INT32_MIN, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("-2147483648", buf)) {
+ printf("INT32_MIN didn't print correctly fmt_long, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_bool(1, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("true", buf) {
+ printf("1 didn't print 'true' as expected, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_bool(-1, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("true", buf) {
+ printf("-1 didn't print 'true' as expected, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ k = print_bool(, buf);
+ if (strlen(buf) != k) printf("length error\n");
+ if (strcmp("false", buf) {
+ printf("0 didn't print 'false' as expected, got:\n'%s'\n", buf);
+ ++failed;
+ }
+ if (failed) {
+ printf("FAILED\n");
+ return -1;
+ }
+ printf("SUCCESS\n");
+ return 0;
+}
+#endif /* PPRINTINT_TEST */
+
+#include "pdiagnostic_pop.h"
+
+#endif /* PPRINTINT_H */
diff --git a/Source/library/FBSUtil/flatcc/portable/pstatic_assert.h b/Source/library/FBSUtil/flatcc/portable/pstatic_assert.h
new file mode 100644
index 0000000..78bc86c
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pstatic_assert.h
@@ -0,0 +1,35 @@
+#ifndef PSTATIC_ASSERT_H
+#define PSTATIC_ASSERT_H
+
+#include
+
+#ifndef static_assert
+
+#ifndef __has_feature
+ #define __has_feature(x) 0
+#endif
+
+#if __has_feature(c_static_assert)
+
+#define static_assert(pred, msg) _Static_assert(pred, msg)
+
+#else
+
+#define __PSTATIC_ASSERT_CONCAT_(a, b) static_assert_scope_##a##_line_##b
+#define __PSTATIC_ASSERT_CONCAT(a, b) __PSTATIC_ASSERT_CONCAT_(a, b)
+#ifdef __COUNTER__
+#define static_assert(e, msg) enum { __PSTATIC_ASSERT_CONCAT(__COUNTER__, __LINE__) = 1/(!!(e)) }
+#else
+#include "pstatic_assert_scope.h"
+#define static_assert(e, msg) enum { __PSTATIC_ASSERT_CONCAT(__PSTATIC_ASSERT_COUNTER, __LINE__) = 1/(!!(e)) }
+#endif
+
+#endif /* __has_feature */
+#endif /* static_assert */
+
+#endif /* PSTATIC_ASSERT_H */
+
+/* Update scope counter outside of include guard. */
+#ifdef __PSTATIC_ASSERT_COUNTER
+#include "pstatic_assert_scope.h"
+#endif
diff --git a/Source/library/FBSUtil/flatcc/portable/pstatic_assert_scope.h b/Source/library/FBSUtil/flatcc/portable/pstatic_assert_scope.h
new file mode 100644
index 0000000..71a0c29
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pstatic_assert_scope.h
@@ -0,0 +1,280 @@
+/*
+ * january, 2017, ported to portable library by mikkelfj.
+ * Based on dbgtools static assert counter, but with renamed macros.
+ */
+
+/*
+ dbgtools - platform independent wrapping of "nice to have" debug functions.
+
+ version 0.1, october, 2013
+
+ https://github.com/wc-duck/dbgtools
+
+ Copyright (C) 2013- Fredrik Kihlander
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Fredrik Kihlander
+*/
+
+/**
+ * Auto-generated header implementing a counter that increases by each include of the file.
+ *
+ * This header will define the macro __PSTATIC_ASSERT_COUNTER to be increased for each inclusion of the file.
+ *
+ * It has been generated with 3 amount of digits resulting in the counter wrapping around after
+ * 10000 inclusions.
+ *
+ * Usage:
+ *
+ * #include "this_header.h"
+ * int a = __PSTATIC_ASSERT_COUNTER; // 0
+ * #include "this_header.h"
+ * int b = __PSTATIC_ASSERT_COUNTER; // 1
+ * #include "this_header.h"
+ * int c = __PSTATIC_ASSERT_COUNTER; // 2
+ * #include "this_header.h"
+ * int d = __PSTATIC_ASSERT_COUNTER; // 3
+ */
+
+#ifndef __PSTATIC_ASSERT_COUNTER
+# define __PSTATIC_ASSERT_COUNTER_0 0
+# define __PSTATIC_ASSERT_COUNTER_1
+# define __PSTATIC_ASSERT_COUNTER_2
+# define __PSTATIC_ASSERT_COUNTER_3
+# define __PSTATIC_ASSERT_COUNTER_D1_0
+# define __PSTATIC_ASSERT_COUNTER_D2_0
+# define __PSTATIC_ASSERT_COUNTER_D3_0
+#endif /* __PSTATIC_ASSERT_COUNTER */
+
+#if !defined( __PSTATIC_ASSERT_COUNTER_D0_0 )
+# define __PSTATIC_ASSERT_COUNTER_D0_0
+# undef __PSTATIC_ASSERT_COUNTER_0
+# define __PSTATIC_ASSERT_COUNTER_0 0
+#elif !defined( __PSTATIC_ASSERT_COUNTER_D0_1 )
+# define __PSTATIC_ASSERT_COUNTER_D0_1
+# undef __PSTATIC_ASSERT_COUNTER_0
+# define __PSTATIC_ASSERT_COUNTER_0 1
+#elif !defined( __PSTATIC_ASSERT_COUNTER_D0_2 )
+# define __PSTATIC_ASSERT_COUNTER_D0_2
+# undef __PSTATIC_ASSERT_COUNTER_0
+# define __PSTATIC_ASSERT_COUNTER_0 2
+#elif !defined( __PSTATIC_ASSERT_COUNTER_D0_3 )
+# define __PSTATIC_ASSERT_COUNTER_D0_3
+# undef __PSTATIC_ASSERT_COUNTER_0
+# define __PSTATIC_ASSERT_COUNTER_0 3
+#elif !defined( __PSTATIC_ASSERT_COUNTER_D0_4 )
+# define __PSTATIC_ASSERT_COUNTER_D0_4
+# undef __PSTATIC_ASSERT_COUNTER_0
+# define __PSTATIC_ASSERT_COUNTER_0 4
+#elif !defined( __PSTATIC_ASSERT_COUNTER_D0_5 )
+# define __PSTATIC_ASSERT_COUNTER_D0_5
+# undef __PSTATIC_ASSERT_COUNTER_0
+# define __PSTATIC_ASSERT_COUNTER_0 5
+#elif !defined( __PSTATIC_ASSERT_COUNTER_D0_6 )
+# define __PSTATIC_ASSERT_COUNTER_D0_6
+# undef __PSTATIC_ASSERT_COUNTER_0
+# define __PSTATIC_ASSERT_COUNTER_0 6
+#elif !defined( __PSTATIC_ASSERT_COUNTER_D0_7 )
+# define __PSTATIC_ASSERT_COUNTER_D0_7
+# undef __PSTATIC_ASSERT_COUNTER_0
+# define __PSTATIC_ASSERT_COUNTER_0 7
+#elif !defined( __PSTATIC_ASSERT_COUNTER_D0_8 )
+# define __PSTATIC_ASSERT_COUNTER_D0_8
+# undef __PSTATIC_ASSERT_COUNTER_0
+# define __PSTATIC_ASSERT_COUNTER_0 8
+#elif !defined( __PSTATIC_ASSERT_COUNTER_D0_9 )
+# define __PSTATIC_ASSERT_COUNTER_D0_9
+# undef __PSTATIC_ASSERT_COUNTER_0
+# define __PSTATIC_ASSERT_COUNTER_0 9
+#else
+# undef __PSTATIC_ASSERT_COUNTER_D0_1
+# undef __PSTATIC_ASSERT_COUNTER_D0_2
+# undef __PSTATIC_ASSERT_COUNTER_D0_3
+# undef __PSTATIC_ASSERT_COUNTER_D0_4
+# undef __PSTATIC_ASSERT_COUNTER_D0_5
+# undef __PSTATIC_ASSERT_COUNTER_D0_6
+# undef __PSTATIC_ASSERT_COUNTER_D0_7
+# undef __PSTATIC_ASSERT_COUNTER_D0_8
+# undef __PSTATIC_ASSERT_COUNTER_D0_9
+# undef __PSTATIC_ASSERT_COUNTER_0
+# define __PSTATIC_ASSERT_COUNTER_0 0
+# if !defined( __PSTATIC_ASSERT_COUNTER_D1_0 )
+# define __PSTATIC_ASSERT_COUNTER_D1_0
+# undef __PSTATIC_ASSERT_COUNTER_1
+# define __PSTATIC_ASSERT_COUNTER_1 0
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D1_1 )
+# define __PSTATIC_ASSERT_COUNTER_D1_1
+# undef __PSTATIC_ASSERT_COUNTER_1
+# define __PSTATIC_ASSERT_COUNTER_1 1
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D1_2 )
+# define __PSTATIC_ASSERT_COUNTER_D1_2
+# undef __PSTATIC_ASSERT_COUNTER_1
+# define __PSTATIC_ASSERT_COUNTER_1 2
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D1_3 )
+# define __PSTATIC_ASSERT_COUNTER_D1_3
+# undef __PSTATIC_ASSERT_COUNTER_1
+# define __PSTATIC_ASSERT_COUNTER_1 3
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D1_4 )
+# define __PSTATIC_ASSERT_COUNTER_D1_4
+# undef __PSTATIC_ASSERT_COUNTER_1
+# define __PSTATIC_ASSERT_COUNTER_1 4
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D1_5 )
+# define __PSTATIC_ASSERT_COUNTER_D1_5
+# undef __PSTATIC_ASSERT_COUNTER_1
+# define __PSTATIC_ASSERT_COUNTER_1 5
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D1_6 )
+# define __PSTATIC_ASSERT_COUNTER_D1_6
+# undef __PSTATIC_ASSERT_COUNTER_1
+# define __PSTATIC_ASSERT_COUNTER_1 6
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D1_7 )
+# define __PSTATIC_ASSERT_COUNTER_D1_7
+# undef __PSTATIC_ASSERT_COUNTER_1
+# define __PSTATIC_ASSERT_COUNTER_1 7
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D1_8 )
+# define __PSTATIC_ASSERT_COUNTER_D1_8
+# undef __PSTATIC_ASSERT_COUNTER_1
+# define __PSTATIC_ASSERT_COUNTER_1 8
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D1_9 )
+# define __PSTATIC_ASSERT_COUNTER_D1_9
+# undef __PSTATIC_ASSERT_COUNTER_1
+# define __PSTATIC_ASSERT_COUNTER_1 9
+# else
+# undef __PSTATIC_ASSERT_COUNTER_D1_1
+# undef __PSTATIC_ASSERT_COUNTER_D1_2
+# undef __PSTATIC_ASSERT_COUNTER_D1_3
+# undef __PSTATIC_ASSERT_COUNTER_D1_4
+# undef __PSTATIC_ASSERT_COUNTER_D1_5
+# undef __PSTATIC_ASSERT_COUNTER_D1_6
+# undef __PSTATIC_ASSERT_COUNTER_D1_7
+# undef __PSTATIC_ASSERT_COUNTER_D1_8
+# undef __PSTATIC_ASSERT_COUNTER_D1_9
+# undef __PSTATIC_ASSERT_COUNTER_1
+# define __PSTATIC_ASSERT_COUNTER_1 0
+# if !defined( __PSTATIC_ASSERT_COUNTER_D2_0 )
+# define __PSTATIC_ASSERT_COUNTER_D2_0
+# undef __PSTATIC_ASSERT_COUNTER_2
+# define __PSTATIC_ASSERT_COUNTER_2 0
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D2_1 )
+# define __PSTATIC_ASSERT_COUNTER_D2_1
+# undef __PSTATIC_ASSERT_COUNTER_2
+# define __PSTATIC_ASSERT_COUNTER_2 1
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D2_2 )
+# define __PSTATIC_ASSERT_COUNTER_D2_2
+# undef __PSTATIC_ASSERT_COUNTER_2
+# define __PSTATIC_ASSERT_COUNTER_2 2
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D2_3 )
+# define __PSTATIC_ASSERT_COUNTER_D2_3
+# undef __PSTATIC_ASSERT_COUNTER_2
+# define __PSTATIC_ASSERT_COUNTER_2 3
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D2_4 )
+# define __PSTATIC_ASSERT_COUNTER_D2_4
+# undef __PSTATIC_ASSERT_COUNTER_2
+# define __PSTATIC_ASSERT_COUNTER_2 4
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D2_5 )
+# define __PSTATIC_ASSERT_COUNTER_D2_5
+# undef __PSTATIC_ASSERT_COUNTER_2
+# define __PSTATIC_ASSERT_COUNTER_2 5
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D2_6 )
+# define __PSTATIC_ASSERT_COUNTER_D2_6
+# undef __PSTATIC_ASSERT_COUNTER_2
+# define __PSTATIC_ASSERT_COUNTER_2 6
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D2_7 )
+# define __PSTATIC_ASSERT_COUNTER_D2_7
+# undef __PSTATIC_ASSERT_COUNTER_2
+# define __PSTATIC_ASSERT_COUNTER_2 7
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D2_8 )
+# define __PSTATIC_ASSERT_COUNTER_D2_8
+# undef __PSTATIC_ASSERT_COUNTER_2
+# define __PSTATIC_ASSERT_COUNTER_2 8
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D2_9 )
+# define __PSTATIC_ASSERT_COUNTER_D2_9
+# undef __PSTATIC_ASSERT_COUNTER_2
+# define __PSTATIC_ASSERT_COUNTER_2 9
+# else
+# undef __PSTATIC_ASSERT_COUNTER_D2_1
+# undef __PSTATIC_ASSERT_COUNTER_D2_2
+# undef __PSTATIC_ASSERT_COUNTER_D2_3
+# undef __PSTATIC_ASSERT_COUNTER_D2_4
+# undef __PSTATIC_ASSERT_COUNTER_D2_5
+# undef __PSTATIC_ASSERT_COUNTER_D2_6
+# undef __PSTATIC_ASSERT_COUNTER_D2_7
+# undef __PSTATIC_ASSERT_COUNTER_D2_8
+# undef __PSTATIC_ASSERT_COUNTER_D2_9
+# undef __PSTATIC_ASSERT_COUNTER_2
+# define __PSTATIC_ASSERT_COUNTER_2 0
+# if !defined( __PSTATIC_ASSERT_COUNTER_D3_0 )
+# define __PSTATIC_ASSERT_COUNTER_D3_0
+# undef __PSTATIC_ASSERT_COUNTER_3
+# define __PSTATIC_ASSERT_COUNTER_3 0
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D3_1 )
+# define __PSTATIC_ASSERT_COUNTER_D3_1
+# undef __PSTATIC_ASSERT_COUNTER_3
+# define __PSTATIC_ASSERT_COUNTER_3 1
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D3_2 )
+# define __PSTATIC_ASSERT_COUNTER_D3_2
+# undef __PSTATIC_ASSERT_COUNTER_3
+# define __PSTATIC_ASSERT_COUNTER_3 2
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D3_3 )
+# define __PSTATIC_ASSERT_COUNTER_D3_3
+# undef __PSTATIC_ASSERT_COUNTER_3
+# define __PSTATIC_ASSERT_COUNTER_3 3
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D3_4 )
+# define __PSTATIC_ASSERT_COUNTER_D3_4
+# undef __PSTATIC_ASSERT_COUNTER_3
+# define __PSTATIC_ASSERT_COUNTER_3 4
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D3_5 )
+# define __PSTATIC_ASSERT_COUNTER_D3_5
+# undef __PSTATIC_ASSERT_COUNTER_3
+# define __PSTATIC_ASSERT_COUNTER_3 5
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D3_6 )
+# define __PSTATIC_ASSERT_COUNTER_D3_6
+# undef __PSTATIC_ASSERT_COUNTER_3
+# define __PSTATIC_ASSERT_COUNTER_3 6
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D3_7 )
+# define __PSTATIC_ASSERT_COUNTER_D3_7
+# undef __PSTATIC_ASSERT_COUNTER_3
+# define __PSTATIC_ASSERT_COUNTER_3 7
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D3_8 )
+# define __PSTATIC_ASSERT_COUNTER_D3_8
+# undef __PSTATIC_ASSERT_COUNTER_3
+# define __PSTATIC_ASSERT_COUNTER_3 8
+# elif !defined( __PSTATIC_ASSERT_COUNTER_D3_9 )
+# define __PSTATIC_ASSERT_COUNTER_D3_9
+# undef __PSTATIC_ASSERT_COUNTER_3
+# define __PSTATIC_ASSERT_COUNTER_3 9
+# else
+# undef __PSTATIC_ASSERT_COUNTER_D3_1
+# undef __PSTATIC_ASSERT_COUNTER_D3_2
+# undef __PSTATIC_ASSERT_COUNTER_D3_3
+# undef __PSTATIC_ASSERT_COUNTER_D3_4
+# undef __PSTATIC_ASSERT_COUNTER_D3_5
+# undef __PSTATIC_ASSERT_COUNTER_D3_6
+# undef __PSTATIC_ASSERT_COUNTER_D3_7
+# undef __PSTATIC_ASSERT_COUNTER_D3_8
+# undef __PSTATIC_ASSERT_COUNTER_D3_9
+# undef __PSTATIC_ASSERT_COUNTER_3
+# define __PSTATIC_ASSERT_COUNTER_3 0
+# endif
+# endif
+# endif
+#endif
+
+#define __PSTATIC_ASSERT_COUNTER_JOIN_DIGITS_MACRO_(digit0,digit1,digit2,digit3) digit0##digit1##digit2##digit3
+#define __PSTATIC_ASSERT_COUNTER_JOIN_DIGITS_MACRO(digit0,digit1,digit2,digit3) __PSTATIC_ASSERT_COUNTER_JOIN_DIGITS_MACRO_(digit0,digit1,digit2,digit3)
+#undef __PSTATIC_ASSERT_COUNTER
+#define __PSTATIC_ASSERT_COUNTER __PSTATIC_ASSERT_COUNTER_JOIN_DIGITS_MACRO(__PSTATIC_ASSERT_COUNTER_3,__PSTATIC_ASSERT_COUNTER_2,__PSTATIC_ASSERT_COUNTER_1,__PSTATIC_ASSERT_COUNTER_0)
diff --git a/Source/library/FBSUtil/flatcc/portable/pstdalign.h b/Source/library/FBSUtil/flatcc/portable/pstdalign.h
new file mode 100644
index 0000000..9dec968
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pstdalign.h
@@ -0,0 +1,63 @@
+#ifndef PSTDALIGN_H
+#define PSTDALIGN_H
+
+/*
+ * NOTE: aligned_alloc is defined via paligned_alloc.h
+ * and requires aligned_free to be fully portable although
+ * free also works on C11 and platforms with posix_memalign.
+ *
+ * NOTE: C++11 defines alignas as a keyword but then also defines
+ * __alignas_is_defined.
+ */
+
+#ifndef __alignas_is_defined
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if (!defined(__clang__) && defined(__GNUC__) && \
+ ((__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 7)))
+#undef PORTABLE_C11_STDALIGN_MISSING
+#define PORTABLE_C11_STDALIGN_MISSING
+#endif
+
+#if defined(__IBMC__)
+#undef PORTABLE_C11_STDALIGN_MISSING
+#define PORTABLE_C11_STDALIGN_MISSING
+#endif
+
+#if ((defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) && \
+ !defined(PORTABLE_C11_STDALIGN_MISSING))
+/* C11 or newer */
+#include
+#else
+#if defined(__GNUC__) || defined(__IBM_ALIGNOF__) || defined(__clang__)
+#define _Alignas(t) __attribute__((__aligned__(t)))
+#define _Alignof(t) __alignof__(t)
+#elif defined(_MSC_VER)
+#define _Alignas(t) __declspec (align(t))
+#define _Alignof(t) __alignof(t)
+
+#else
+#error please update pstdalign.h with support for current compiler
+#endif
+
+#endif /* __STDC__ */
+
+#define alignas _Alignas
+#define alignof _Alignof
+
+#define __alignas_is_defined 1
+#define __alignof_is_defined 1
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __alignas__is_defined */
+
+#include "paligned_alloc.h"
+
+
+#endif /* PSTDALIGN_H */
diff --git a/Source/library/FBSUtil/flatcc/portable/pstdbool.h b/Source/library/FBSUtil/flatcc/portable/pstdbool.h
new file mode 100644
index 0000000..28fc89c
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pstdbool.h
@@ -0,0 +1,37 @@
+#ifndef PSTDBOOL_H
+#define PSTDBOOL_H
+
+#if !defined(__cplusplus) && !__bool_true_false_are_defined && !defined(bool) && !defined(__STDBOOL_H)
+
+#ifdef HAVE_STDBOOL_H
+
+#include
+
+#elif (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
+/* C99 or newer */
+
+#define bool _Bool
+#define true 1
+#define false 0
+#define __bool_true_false_are_defined 1
+
+#elif defined(__GNUC__) && !defined(__STRICT_ANSI__)
+
+#define bool bool
+#define true true
+#define false false
+#define __bool_true_false_are_defined 1
+
+#else
+
+typedef unsigned char _Portable_bool;
+#define bool _Portable_bool
+#define true 1
+#define false 0
+#define __bool_true_false_are_defined 1
+
+#endif
+
+#endif
+
+#endif /* PSTDBOOL_H */
diff --git a/Source/library/FBSUtil/flatcc/portable/pstdint.h b/Source/library/FBSUtil/flatcc/portable/pstdint.h
new file mode 100644
index 0000000..14444aa
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pstdint.h
@@ -0,0 +1,898 @@
+/* A portable stdint.h
+ ****************************************************************************
+ * BSD License:
+ ****************************************************************************
+ *
+ * Copyright (c) 2005-2016 Paul Hsieh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************
+ *
+ * Version 0.1.15.2
+ *
+ * The ANSI C standard committee, for the C99 standard, specified the
+ * inclusion of a new standard include file called stdint.h. This is
+ * a very useful and long desired include file which contains several
+ * very precise definitions for integer scalar types that is
+ * critically important for making portable several classes of
+ * applications including cryptography, hashing, variable length
+ * integer libraries and so on. But for most developers its likely
+ * useful just for programming sanity.
+ *
+ * The problem is that some compiler vendors chose to ignore the C99
+ * standard and some older compilers have no opportunity to be updated.
+ * Because of this situation, simply including stdint.h in your code
+ * makes it unportable.
+ *
+ * So that's what this file is all about. Its an attempt to build a
+ * single universal include file that works on as many platforms as
+ * possible to deliver what stdint.h is supposed to. Even compilers
+ * that already come with stdint.h can use this file instead without
+ * any loss of functionality. A few things that should be noted about
+ * this file:
+ *
+ * 1) It is not guaranteed to be portable and/or present an identical
+ * interface on all platforms. The extreme variability of the
+ * ANSI C standard makes this an impossibility right from the
+ * very get go. Its really only meant to be useful for the vast
+ * majority of platforms that possess the capability of
+ * implementing usefully and precisely defined, standard sized
+ * integer scalars. Systems which are not intrinsically 2s
+ * complement may produce invalid constants.
+ *
+ * 2) There is an unavoidable use of non-reserved symbols.
+ *
+ * 3) Other standard include files are invoked.
+ *
+ * 4) This file may come in conflict with future platforms that do
+ * include stdint.h. The hope is that one or the other can be
+ * used with no real difference.
+ *
+ * 5) In the current verison, if your platform can't represent
+ * int32_t, int16_t and int8_t, it just dumps out with a compiler
+ * error.
+ *
+ * 6) 64 bit integers may or may not be defined. Test for their
+ * presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX.
+ * Note that this is different from the C99 specification which
+ * requires the existence of 64 bit support in the compiler. If
+ * this is not defined for your platform, yet it is capable of
+ * dealing with 64 bits then it is because this file has not yet
+ * been extended to cover all of your system's capabilities.
+ *
+ * 7) (u)intptr_t may or may not be defined. Test for its presence
+ * with the test: #ifdef PTRDIFF_MAX. If this is not defined
+ * for your platform, then it is because this file has not yet
+ * been extended to cover all of your system's capabilities, not
+ * because its optional.
+ *
+ * 8) The following might not been defined even if your platform is
+ * capable of defining it:
+ *
+ * WCHAR_MIN
+ * WCHAR_MAX
+ * (u)int64_t
+ * PTRDIFF_MIN
+ * PTRDIFF_MAX
+ * (u)intptr_t
+ *
+ * 9) The following have not been defined:
+ *
+ * WINT_MIN
+ * WINT_MAX
+ *
+ * 10) The criteria for defining (u)int_least(*)_t isn't clear,
+ * except for systems which don't have a type that precisely
+ * defined 8, 16, or 32 bit types (which this include file does
+ * not support anyways). Default definitions have been given.
+ *
+ * 11) The criteria for defining (u)int_fast(*)_t isn't something I
+ * would trust to any particular compiler vendor or the ANSI C
+ * committee. It is well known that "compatible systems" are
+ * commonly created that have very different performance
+ * characteristics from the systems they are compatible with,
+ * especially those whose vendors make both the compiler and the
+ * system. Default definitions have been given, but its strongly
+ * recommended that users never use these definitions for any
+ * reason (they do *NOT* deliver any serious guarantee of
+ * improved performance -- not in this file, nor any vendor's
+ * stdint.h).
+ *
+ * 12) The following macros:
+ *
+ * PRINTF_INTMAX_MODIFIER
+ * PRINTF_INT64_MODIFIER
+ * PRINTF_INT32_MODIFIER
+ * PRINTF_INT16_MODIFIER
+ * PRINTF_LEAST64_MODIFIER
+ * PRINTF_LEAST32_MODIFIER
+ * PRINTF_LEAST16_MODIFIER
+ * PRINTF_INTPTR_MODIFIER
+ *
+ * are strings which have been defined as the modifiers required
+ * for the "d", "u" and "x" printf formats to correctly output
+ * (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t,
+ * (u)least32_t, (u)least16_t and (u)intptr_t types respectively.
+ * PRINTF_INTPTR_MODIFIER is not defined for some systems which
+ * provide their own stdint.h. PRINTF_INT64_MODIFIER is not
+ * defined if INT64_MAX is not defined. These are an extension
+ * beyond what C99 specifies must be in stdint.h.
+ *
+ * In addition, the following macros are defined:
+ *
+ * PRINTF_INTMAX_HEX_WIDTH
+ * PRINTF_INT64_HEX_WIDTH
+ * PRINTF_INT32_HEX_WIDTH
+ * PRINTF_INT16_HEX_WIDTH
+ * PRINTF_INT8_HEX_WIDTH
+ * PRINTF_INTMAX_DEC_WIDTH
+ * PRINTF_INT64_DEC_WIDTH
+ * PRINTF_INT32_DEC_WIDTH
+ * PRINTF_INT16_DEC_WIDTH
+ * PRINTF_UINT8_DEC_WIDTH
+ * PRINTF_UINTMAX_DEC_WIDTH
+ * PRINTF_UINT64_DEC_WIDTH
+ * PRINTF_UINT32_DEC_WIDTH
+ * PRINTF_UINT16_DEC_WIDTH
+ * PRINTF_UINT8_DEC_WIDTH
+ *
+ * Which specifies the maximum number of characters required to
+ * print the number of that type in either hexadecimal or decimal.
+ * These are an extension beyond what C99 specifies must be in
+ * stdint.h.
+ *
+ * Compilers tested (all with 0 warnings at their highest respective
+ * settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32
+ * bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio
+ * .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3
+ *
+ * This file should be considered a work in progress. Suggestions for
+ * improvements, especially those which increase coverage are strongly
+ * encouraged.
+ *
+ * Acknowledgements
+ *
+ * The following people have made significant contributions to the
+ * development and testing of this file:
+ *
+ * Chris Howie
+ * John Steele Scott
+ * Dave Thorup
+ * John Dill
+ * Florian Wobbe
+ * Christopher Sean Morrison
+ * Mikkel Fahnoe Jorgensen
+ *
+ */
+
+#include
+#include
+#include
+
+/*
+ * For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and
+ * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_.
+ */
+
+#if ((defined(_MSC_VER) && _MSC_VER >= 1600) || (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (__GNUC__ > 3 || defined(_STDINT_H) || defined(_STDINT_H_) || defined (__UINT_FAST64_TYPE__)) )) && !defined (_PSTDINT_H_INCLUDED)
+#include
+#define _PSTDINT_H_INCLUDED
+# if defined(__GNUC__) && (defined(__x86_64__) || defined(__ppc64__)) && !(defined(__APPLE__) && defined(__MACH__))
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "l"
+# endif
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER ""
+# endif
+# else
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "ll"
+# endif
+# ifndef PRINTF_INT32_MODIFIER
+# if (UINT_MAX == UINT32_MAX)
+# define PRINTF_INT32_MODIFIER ""
+# else
+# define PRINTF_INT32_MODIFIER "l"
+# endif
+# endif
+# endif
+# ifndef PRINTF_INT16_MODIFIER
+# define PRINTF_INT16_MODIFIER "h"
+# endif
+# ifndef PRINTF_INTMAX_MODIFIER
+# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER
+# endif
+# ifndef PRINTF_INT64_HEX_WIDTH
+# define PRINTF_INT64_HEX_WIDTH "16"
+# endif
+# ifndef PRINTF_UINT64_HEX_WIDTH
+# define PRINTF_UINT64_HEX_WIDTH "16"
+# endif
+# ifndef PRINTF_INT32_HEX_WIDTH
+# define PRINTF_INT32_HEX_WIDTH "8"
+# endif
+# ifndef PRINTF_UINT32_HEX_WIDTH
+# define PRINTF_UINT32_HEX_WIDTH "8"
+# endif
+# ifndef PRINTF_INT16_HEX_WIDTH
+# define PRINTF_INT16_HEX_WIDTH "4"
+# endif
+# ifndef PRINTF_UINT16_HEX_WIDTH
+# define PRINTF_UINT16_HEX_WIDTH "4"
+# endif
+# ifndef PRINTF_INT8_HEX_WIDTH
+# define PRINTF_INT8_HEX_WIDTH "2"
+# endif
+# ifndef PRINTF_UINT8_HEX_WIDTH
+# define PRINTF_UINT8_HEX_WIDTH "2"
+# endif
+# ifndef PRINTF_INT64_DEC_WIDTH
+# define PRINTF_INT64_DEC_WIDTH "19"
+# endif
+# ifndef PRINTF_UINT64_DEC_WIDTH
+# define PRINTF_UINT64_DEC_WIDTH "20"
+# endif
+# ifndef PRINTF_INT32_DEC_WIDTH
+# define PRINTF_INT32_DEC_WIDTH "10"
+# endif
+# ifndef PRINTF_UINT32_DEC_WIDTH
+# define PRINTF_UINT32_DEC_WIDTH "10"
+# endif
+# ifndef PRINTF_INT16_DEC_WIDTH
+# define PRINTF_INT16_DEC_WIDTH "5"
+# endif
+# ifndef PRINTF_UINT16_DEC_WIDTH
+# define PRINTF_UINT16_DEC_WIDTH "5"
+# endif
+# ifndef PRINTF_INT8_DEC_WIDTH
+# define PRINTF_INT8_DEC_WIDTH "3"
+# endif
+# ifndef PRINTF_UINT8_DEC_WIDTH
+# define PRINTF_UINT8_DEC_WIDTH "3"
+# endif
+# ifndef PRINTF_INTMAX_HEX_WIDTH
+# define PRINTF_INTMAX_HEX_WIDTH PRINTF_UINT64_HEX_WIDTH
+# endif
+# ifndef PRINTF_UINTMAX_HEX_WIDTH
+# define PRINTF_UINTMAX_HEX_WIDTH PRINTF_UINT64_HEX_WIDTH
+# endif
+# ifndef PRINTF_INTMAX_DEC_WIDTH
+# define PRINTF_INTMAX_DEC_WIDTH PRINTF_UINT64_DEC_WIDTH
+# endif
+# ifndef PRINTF_UINTMAX_DEC_WIDTH
+# define PRINTF_UINTMAX_DEC_WIDTH PRINTF_UINT64_DEC_WIDTH
+# endif
+
+/*
+ * Something really weird is going on with Open Watcom. Just pull some of
+ * these duplicated definitions from Open Watcom's stdint.h file for now.
+ */
+
+# if defined (__WATCOMC__) && __WATCOMC__ >= 1250
+# if !defined (INT64_C)
+# define INT64_C(x) (x + (INT64_MAX - INT64_MAX))
+# endif
+# if !defined (UINT64_C)
+# define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX))
+# endif
+# if !defined (INT32_C)
+# define INT32_C(x) (x + (INT32_MAX - INT32_MAX))
+# endif
+# if !defined (UINT32_C)
+# define UINT32_C(x) (x + (UINT32_MAX - UINT32_MAX))
+# endif
+# if !defined (INT16_C)
+# define INT16_C(x) (x)
+# endif
+# if !defined (UINT16_C)
+# define UINT16_C(x) (x)
+# endif
+# if !defined (INT8_C)
+# define INT8_C(x) (x)
+# endif
+# if !defined (UINT8_C)
+# define UINT8_C(x) (x)
+# endif
+# if !defined (UINT64_MAX)
+# define UINT64_MAX 18446744073709551615ULL
+# endif
+# if !defined (INT64_MAX)
+# define INT64_MAX 9223372036854775807LL
+# endif
+# if !defined (UINT32_MAX)
+# define UINT32_MAX 4294967295UL
+# endif
+# if !defined (INT32_MAX)
+# define INT32_MAX 2147483647L
+# endif
+# if !defined (INTMAX_MAX)
+# define INTMAX_MAX INT64_MAX
+# endif
+# if !defined (INTMAX_MIN)
+# define INTMAX_MIN INT64_MIN
+# endif
+# endif
+#endif
+
+#ifndef _PSTDINT_H_INCLUDED
+#define _PSTDINT_H_INCLUDED
+
+#ifndef SIZE_MAX
+# define SIZE_MAX (~(size_t)0)
+#endif
+
+/*
+ * Deduce the type assignments from limits.h under the assumption that
+ * integer sizes in bits are powers of 2, and follow the ANSI
+ * definitions.
+ */
+
+#ifndef UINT8_MAX
+# define UINT8_MAX 0xff
+#endif
+#if !defined(uint8_t) && !defined(_UINT8_T)
+# if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S)
+ typedef unsigned char uint8_t;
+# define UINT8_C(v) ((uint8_t) v)
+# else
+# error "Platform not supported"
+# endif
+#endif
+
+#ifndef INT8_MAX
+# define INT8_MAX 0x7f
+#endif
+#ifndef INT8_MIN
+# define INT8_MIN INT8_C(0x80)
+#endif
+#if !defined(int8_t) && !defined(_INT8_T)
+# if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S)
+ typedef signed char int8_t;
+# define INT8_C(v) ((int8_t) v)
+# else
+# error "Platform not supported"
+# endif
+#endif
+
+#ifndef UINT16_MAX
+# define UINT16_MAX 0xffff
+#endif
+#if !defined(uint16_t) && !defined(_UINT16_T)
+#if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S)
+ typedef unsigned int uint16_t;
+# ifndef PRINTF_INT16_MODIFIER
+# define PRINTF_INT16_MODIFIER ""
+# endif
+# define UINT16_C(v) ((uint16_t) (v))
+#elif (USHRT_MAX == UINT16_MAX)
+ typedef unsigned short uint16_t;
+# define UINT16_C(v) ((uint16_t) (v))
+# ifndef PRINTF_INT16_MODIFIER
+# define PRINTF_INT16_MODIFIER "h"
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+#ifndef INT16_MAX
+# define INT16_MAX 0x7fff
+#endif
+#ifndef INT16_MIN
+# define INT16_MIN INT16_C(0x8000)
+#endif
+#if !defined(int16_t) && !defined(_INT16_T)
+#if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S)
+ typedef signed int int16_t;
+# define INT16_C(v) ((int16_t) (v))
+# ifndef PRINTF_INT16_MODIFIER
+# define PRINTF_INT16_MODIFIER ""
+# endif
+#elif (SHRT_MAX == INT16_MAX)
+ typedef signed short int16_t;
+# define INT16_C(v) ((int16_t) (v))
+# ifndef PRINTF_INT16_MODIFIER
+# define PRINTF_INT16_MODIFIER "h"
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+#ifndef UINT32_MAX
+# define UINT32_MAX (0xffffffffUL)
+#endif
+#if !defined(uint32_t) && !defined(_UINT32_T)
+#if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S)
+ typedef unsigned long uint32_t;
+# define UINT32_C(v) v ## UL
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER "l"
+# endif
+#elif (UINT_MAX == UINT32_MAX)
+ typedef unsigned int uint32_t;
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER ""
+# endif
+# define UINT32_C(v) v ## U
+#elif (USHRT_MAX == UINT32_MAX)
+ typedef unsigned short uint32_t;
+# define UINT32_C(v) ((unsigned short) (v))
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER ""
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+#ifndef INT32_MAX
+# define INT32_MAX (0x7fffffffL)
+#endif
+#ifndef INT32_MIN
+# define INT32_MIN INT32_C(0x80000000)
+#endif
+#if !defined(int32_t) && !defined(_INT32_T)
+#if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S)
+ typedef signed long int32_t;
+# define INT32_C(v) v ## L
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER "l"
+# endif
+#elif (INT_MAX == INT32_MAX)
+ typedef signed int int32_t;
+# define INT32_C(v) v
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER ""
+# endif
+#elif (SHRT_MAX == INT32_MAX)
+ typedef signed short int32_t;
+# define INT32_C(v) ((short) (v))
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER ""
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+/*
+ * The macro stdint_int64_defined is temporarily used to record
+ * whether or not 64 integer support is available. It must be
+ * defined for any 64 integer extensions for new platforms that are
+ * added.
+ */
+
+#undef stdint_int64_defined
+#if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S)
+# if (__STDC__ && __STDC_VERSION__ >= 199901L) || defined (S_SPLINT_S)
+# define stdint_int64_defined
+ typedef long long int64_t;
+ typedef unsigned long long uint64_t;
+# define UINT64_C(v) v ## ULL
+# define INT64_C(v) v ## LL
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "ll"
+# endif
+# endif
+#endif
+
+#if !defined (stdint_int64_defined)
+# if defined(__GNUC__)
+# define stdint_int64_defined
+ __extension__ typedef long long int64_t;
+ __extension__ typedef unsigned long long uint64_t;
+# define UINT64_C(v) v ## ULL
+# define INT64_C(v) v ## LL
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "ll"
+# endif
+# elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S)
+# define stdint_int64_defined
+ typedef long long int64_t;
+ typedef unsigned long long uint64_t;
+# define UINT64_C(v) v ## ULL
+# define INT64_C(v) v ## LL
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "ll"
+# endif
+# elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC)
+# define stdint_int64_defined
+ typedef __int64 int64_t;
+ typedef unsigned __int64 uint64_t;
+# define UINT64_C(v) v ## UI64
+# define INT64_C(v) v ## I64
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "I64"
+# endif
+# endif
+#endif
+
+#if !defined (LONG_LONG_MAX) && defined (INT64_C)
+# define LONG_LONG_MAX INT64_C (9223372036854775807)
+#endif
+#ifndef ULONG_LONG_MAX
+# define ULONG_LONG_MAX UINT64_C (18446744073709551615)
+#endif
+
+#if !defined (INT64_MAX) && defined (INT64_C)
+# define INT64_MAX INT64_C (9223372036854775807)
+#endif
+#if !defined (INT64_MIN) && defined (INT64_C)
+# define INT64_MIN INT64_C (-9223372036854775808)
+#endif
+#if !defined (UINT64_MAX) && defined (INT64_C)
+# define UINT64_MAX UINT64_C (18446744073709551615)
+#endif
+
+/*
+ * Width of hexadecimal for number field.
+ */
+
+#ifndef PRINTF_INT64_HEX_WIDTH
+# define PRINTF_INT64_HEX_WIDTH "16"
+#endif
+#ifndef PRINTF_INT32_HEX_WIDTH
+# define PRINTF_INT32_HEX_WIDTH "8"
+#endif
+#ifndef PRINTF_INT16_HEX_WIDTH
+# define PRINTF_INT16_HEX_WIDTH "4"
+#endif
+#ifndef PRINTF_INT8_HEX_WIDTH
+# define PRINTF_INT8_HEX_WIDTH "2"
+#endif
+#ifndef PRINTF_INT64_DEC_WIDTH
+# define PRINTF_INT64_DEC_WIDTH "19"
+#endif
+#ifndef PRINTF_INT32_DEC_WIDTH
+# define PRINTF_INT32_DEC_WIDTH "10"
+#endif
+#ifndef PRINTF_INT16_DEC_WIDTH
+# define PRINTF_INT16_DEC_WIDTH "5"
+#endif
+#ifndef PRINTF_INT8_DEC_WIDTH
+# define PRINTF_INT8_DEC_WIDTH "3"
+#endif
+#ifndef PRINTF_UINT64_DEC_WIDTH
+# define PRINTF_UINT64_DEC_WIDTH "20"
+#endif
+#ifndef PRINTF_UINT32_DEC_WIDTH
+# define PRINTF_UINT32_DEC_WIDTH "10"
+#endif
+#ifndef PRINTF_UINT16_DEC_WIDTH
+# define PRINTF_UINT16_DEC_WIDTH "5"
+#endif
+#ifndef PRINTF_UINT8_DEC_WIDTH
+# define PRINTF_UINT8_DEC_WIDTH "3"
+#endif
+
+/*
+ * Ok, lets not worry about 128 bit integers for now. Moore's law says
+ * we don't need to worry about that until about 2040 at which point
+ * we'll have bigger things to worry about.
+ */
+
+#ifdef stdint_int64_defined
+ typedef int64_t intmax_t;
+ typedef uint64_t uintmax_t;
+# define INTMAX_MAX INT64_MAX
+# define INTMAX_MIN INT64_MIN
+# define UINTMAX_MAX UINT64_MAX
+# define UINTMAX_C(v) UINT64_C(v)
+# define INTMAX_C(v) INT64_C(v)
+# ifndef PRINTF_INTMAX_MODIFIER
+# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER
+# endif
+# ifndef PRINTF_INTMAX_HEX_WIDTH
+# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH
+# endif
+# ifndef PRINTF_INTMAX_DEC_WIDTH
+# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH
+# endif
+#else
+ typedef int32_t intmax_t;
+ typedef uint32_t uintmax_t;
+# define INTMAX_MAX INT32_MAX
+# define UINTMAX_MAX UINT32_MAX
+# define UINTMAX_C(v) UINT32_C(v)
+# define INTMAX_C(v) INT32_C(v)
+# ifndef PRINTF_INTMAX_MODIFIER
+# define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER
+# endif
+# ifndef PRINTF_INTMAX_HEX_WIDTH
+# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH
+# endif
+# ifndef PRINTF_INTMAX_DEC_WIDTH
+# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH
+# endif
+#endif
+
+/*
+ * Because this file currently only supports platforms which have
+ * precise powers of 2 as bit sizes for the default integers, the
+ * least definitions are all trivial. Its possible that a future
+ * version of this file could have different definitions.
+ */
+
+#ifndef stdint_least_defined
+ typedef int8_t int_least8_t;
+ typedef uint8_t uint_least8_t;
+ typedef int16_t int_least16_t;
+ typedef uint16_t uint_least16_t;
+ typedef int32_t int_least32_t;
+ typedef uint32_t uint_least32_t;
+# define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER
+# define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER
+# define UINT_LEAST8_MAX UINT8_MAX
+# define INT_LEAST8_MAX INT8_MAX
+# define UINT_LEAST16_MAX UINT16_MAX
+# define INT_LEAST16_MAX INT16_MAX
+# define UINT_LEAST32_MAX UINT32_MAX
+# define INT_LEAST32_MAX INT32_MAX
+# define INT_LEAST8_MIN INT8_MIN
+# define INT_LEAST16_MIN INT16_MIN
+# define INT_LEAST32_MIN INT32_MIN
+# ifdef stdint_int64_defined
+ typedef int64_t int_least64_t;
+ typedef uint64_t uint_least64_t;
+# define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER
+# define UINT_LEAST64_MAX UINT64_MAX
+# define INT_LEAST64_MAX INT64_MAX
+# define INT_LEAST64_MIN INT64_MIN
+# endif
+#endif
+#undef stdint_least_defined
+
+/*
+ * The ANSI C committee pretending to know or specify anything about
+ * performance is the epitome of misguided arrogance. The mandate of
+ * this file is to *ONLY* ever support that absolute minimum
+ * definition of the fast integer types, for compatibility purposes.
+ * No extensions, and no attempt to suggest what may or may not be a
+ * faster integer type will ever be made in this file. Developers are
+ * warned to stay away from these types when using this or any other
+ * stdint.h.
+ */
+
+typedef int_least8_t int_fast8_t;
+typedef uint_least8_t uint_fast8_t;
+typedef int_least16_t int_fast16_t;
+typedef uint_least16_t uint_fast16_t;
+typedef int_least32_t int_fast32_t;
+typedef uint_least32_t uint_fast32_t;
+#define UINT_FAST8_MAX UINT_LEAST8_MAX
+#define INT_FAST8_MAX INT_LEAST8_MAX
+#define UINT_FAST16_MAX UINT_LEAST16_MAX
+#define INT_FAST16_MAX INT_LEAST16_MAX
+#define UINT_FAST32_MAX UINT_LEAST32_MAX
+#define INT_FAST32_MAX INT_LEAST32_MAX
+#define INT_FAST8_MIN INT_LEAST8_MIN
+#define INT_FAST16_MIN INT_LEAST16_MIN
+#define INT_FAST32_MIN INT_LEAST32_MIN
+#ifdef stdint_int64_defined
+ typedef int_least64_t int_fast64_t;
+ typedef uint_least64_t uint_fast64_t;
+# define UINT_FAST64_MAX UINT_LEAST64_MAX
+# define INT_FAST64_MAX INT_LEAST64_MAX
+# define INT_FAST64_MIN INT_LEAST64_MIN
+#endif
+
+#undef stdint_int64_defined
+
+/*
+ * Whatever piecemeal, per compiler thing we can do about the wchar_t
+ * type limits.
+ */
+
+#if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__)
+# include
+# ifndef WCHAR_MIN
+# define WCHAR_MIN 0
+# endif
+# ifndef WCHAR_MAX
+# define WCHAR_MAX ((wchar_t)-1)
+# endif
+#endif
+
+/*
+ * Whatever piecemeal, per compiler/platform thing we can do about the
+ * (u)intptr_t types and limits.
+ */
+
+#if (defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED)) || defined (_UINTPTR_T)
+# define STDINT_H_UINTPTR_T_DEFINED
+#endif
+
+#ifndef STDINT_H_UINTPTR_T_DEFINED
+# if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64) || defined (__ppc64__)
+# define stdint_intptr_bits 64
+# elif defined (__WATCOMC__) || defined (__TURBOC__)
+# if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__)
+# define stdint_intptr_bits 16
+# else
+# define stdint_intptr_bits 32
+# endif
+# elif defined (__i386__) || defined (_WIN32) || defined (WIN32) || defined (__ppc64__)
+# define stdint_intptr_bits 32
+# elif defined (__INTEL_COMPILER)
+/* TODO -- what did Intel do about x86-64? */
+# else
+/* #error "This platform might not be supported yet" */
+# endif
+
+# ifdef stdint_intptr_bits
+# define stdint_intptr_glue3_i(a,b,c) a##b##c
+# define stdint_intptr_glue3(a,b,c) stdint_intptr_glue3_i(a,b,c)
+# ifndef PRINTF_INTPTR_MODIFIER
+# define PRINTF_INTPTR_MODIFIER stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER)
+# endif
+# ifndef PTRDIFF_MAX
+# define PTRDIFF_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX)
+# endif
+# ifndef PTRDIFF_MIN
+# define PTRDIFF_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN)
+# endif
+# ifndef UINTPTR_MAX
+# define UINTPTR_MAX stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX)
+# endif
+# ifndef INTPTR_MAX
+# define INTPTR_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX)
+# endif
+# ifndef INTPTR_MIN
+# define INTPTR_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN)
+# endif
+# ifndef INTPTR_C
+# define INTPTR_C(x) stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x)
+# endif
+# ifndef UINTPTR_C
+# define UINTPTR_C(x) stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x)
+# endif
+ typedef stdint_intptr_glue3(uint,stdint_intptr_bits,_t) uintptr_t;
+ typedef stdint_intptr_glue3( int,stdint_intptr_bits,_t) intptr_t;
+# else
+/* TODO -- This following is likely wrong for some platforms, and does
+ nothing for the definition of uintptr_t. */
+ typedef ptrdiff_t intptr_t;
+# endif
+# define STDINT_H_UINTPTR_T_DEFINED
+#endif
+
+/*
+ * Assumes sig_atomic_t is signed and we have a 2s complement machine.
+ */
+
+#ifndef SIG_ATOMIC_MAX
+# define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1)
+#endif
+
+#endif
+
+#if defined (__TEST_PSTDINT_FOR_CORRECTNESS)
+
+/*
+ * Please compile with the maximum warning settings to make sure macros are
+ * not defined more than once.
+ */
+
+#include
+#include
+#include
+
+#define glue3_aux(x,y,z) x ## y ## z
+#define glue3(x,y,z) glue3_aux(x,y,z)
+
+#define DECLU(bits) glue3(uint,bits,_t) glue3(u,bits,) = glue3(UINT,bits,_C) (0);
+#define DECLI(bits) glue3(int,bits,_t) glue3(i,bits,) = glue3(INT,bits,_C) (0);
+
+#define DECL(us,bits) glue3(DECL,us,) (bits)
+
+#define TESTUMAX(bits) glue3(u,bits,) = ~glue3(u,bits,); if (glue3(UINT,bits,_MAX) != glue3(u,bits,)) printf ("Something wrong with UINT%d_MAX\n", bits)
+
+#define REPORTERROR(msg) { err_n++; if (err_first <= 0) err_first = __LINE__; printf msg; }
+
+int main () {
+ int err_n = 0;
+ int err_first = 0;
+ DECL(I,8)
+ DECL(U,8)
+ DECL(I,16)
+ DECL(U,16)
+ DECL(I,32)
+ DECL(U,32)
+#ifdef INT64_MAX
+ DECL(I,64)
+ DECL(U,64)
+#endif
+ intmax_t imax = INTMAX_C(0);
+ uintmax_t umax = UINTMAX_C(0);
+ char str0[256], str1[256];
+
+ sprintf (str0, "%" PRINTF_INT32_MODIFIER "d", INT32_C(2147483647));
+ if (0 != strcmp (str0, "2147483647")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0));
+ if (atoi(PRINTF_INT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_INT32_DEC_WIDTH : %s\n", PRINTF_INT32_DEC_WIDTH));
+ sprintf (str0, "%" PRINTF_INT32_MODIFIER "u", UINT32_C(4294967295));
+ if (0 != strcmp (str0, "4294967295")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0));
+ if (atoi(PRINTF_UINT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_UINT32_DEC_WIDTH : %s\n", PRINTF_UINT32_DEC_WIDTH));
+#ifdef INT64_MAX
+ sprintf (str1, "%" PRINTF_INT64_MODIFIER "d", INT64_C(9223372036854775807));
+ if (0 != strcmp (str1, "9223372036854775807")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1));
+ if (atoi(PRINTF_INT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_INT64_DEC_WIDTH : %s, %d\n", PRINTF_INT64_DEC_WIDTH, (int) strlen(str1)));
+ sprintf (str1, "%" PRINTF_INT64_MODIFIER "u", UINT64_C(18446744073709550591));
+ if (0 != strcmp (str1, "18446744073709550591")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1));
+ if (atoi(PRINTF_UINT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_UINT64_DEC_WIDTH : %s, %d\n", PRINTF_UINT64_DEC_WIDTH, (int) strlen(str1)));
+#endif
+
+ sprintf (str0, "%d %x\n", 0, ~0);
+
+ sprintf (str1, "%d %x\n", i8, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i8 : %s\n", str1));
+ sprintf (str1, "%u %x\n", u8, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u8 : %s\n", str1));
+ sprintf (str1, "%d %x\n", i16, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i16 : %s\n", str1));
+ sprintf (str1, "%u %x\n", u16, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u16 : %s\n", str1));
+ sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n", i32, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i32 : %s\n", str1));
+ sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n", u32, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u32 : %s\n", str1));
+#ifdef INT64_MAX
+ sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n", i64, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i64 : %s\n", str1));
+#endif
+ sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n", imax, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with imax : %s\n", str1));
+ sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n", umax, ~0);
+ if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with umax : %s\n", str1));
+
+ TESTUMAX(8);
+ TESTUMAX(16);
+ TESTUMAX(32);
+#ifdef INT64_MAX
+ TESTUMAX(64);
+#endif
+
+#define STR(v) #v
+#define Q(v) printf ("sizeof " STR(v) " = %u\n", (unsigned) sizeof (v));
+ if (err_n) {
+ printf ("pstdint.h is not correct. Please use sizes below to correct it:\n");
+ }
+
+ Q(int)
+ Q(unsigned)
+ Q(long int)
+ Q(short int)
+ Q(int8_t)
+ Q(int16_t)
+ Q(int32_t)
+#ifdef INT64_MAX
+ Q(int64_t)
+#endif
+
+ return EXIT_SUCCESS;
+}
+
+#endif
diff --git a/Source/library/FBSUtil/flatcc/portable/punaligned.h b/Source/library/FBSUtil/flatcc/portable/punaligned.h
new file mode 100644
index 0000000..6ed1005
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/punaligned.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2016 Mikkel Fahnøe Jørgensen, dvide.com
+ *
+ * (MIT License)
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ * - The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * - The Software is provided "as is", without warranty of any kind, express or
+ * implied, including but not limited to the warranties of merchantability,
+ * fitness for a particular purpose and noninfringement. In no event shall the
+ * authors or copyright holders be liable for any claim, damages or other
+ * liability, whether in an action of contract, tort or otherwise, arising from,
+ * out of or in connection with the Software or the use or other dealings in the
+ * Software.
+ */
+
+#ifndef PUNLIGNED_H
+#define PUNLIGNED_H
+
+#ifndef PORTABLE_UNALIGNED_ACCESS
+
+#if defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64)
+#define PORTABLE_UNALIGNED_ACCESS 1
+#else
+#define PORTABLE_UNALIGNED_ACCESS 0
+#endif
+
+#endif
+
+/* `unaligned_read_16` might not be defined if endianness was not determined. */
+#if !defined(unaligned_read_le16toh)
+
+#include "pendian.h"
+
+#ifndef UINT8_MAX
+#include
+#endif
+
+#if PORTABLE_UNALIGNED_ACCESS
+
+#define unaligned_read_16(p) (*(uint16_t*)(p))
+#define unaligned_read_32(p) (*(uint32_t*)(p))
+#define unaligned_read_64(p) (*(uint64_t*)(p))
+
+#define unaligned_read_le16toh(p) le16toh(*(uint16_t*)(p))
+#define unaligned_read_le32toh(p) le32toh(*(uint32_t*)(p))
+#define unaligned_read_le64toh(p) le64toh(*(uint64_t*)(p))
+
+#define unaligned_read_be16toh(p) be16toh(*(uint16_t*)(p))
+#define unaligned_read_be32toh(p) be32toh(*(uint32_t*)(p))
+#define unaligned_read_be64toh(p) be64toh(*(uint64_t*)(p))
+
+#define unaligned_write_16(p, v) (*(uint16_t*)(p) = (uint16_t)(v))
+#define unaligned_write_32(p, v) (*(uint32_t*)(p) = (uint32_t)(v))
+#define unaligned_write_64(p, v) (*(uint64_t*)(p) = (uint64_t)(v))
+
+#define unaligned_write_htole16(p, v) (*(uint16_t*)(p) = htole16(v))
+#define unaligned_write_htole32(p, v) (*(uint32_t*)(p) = htole32(v))
+#define unaligned_write_htole64(p, v) (*(uint64_t*)(p) = htole64(v))
+
+#define unaligned_write_htobe16(p, v) (*(uint16_t*)(p) = htobe16(v))
+#define unaligned_write_htobe32(p, v) (*(uint32_t*)(p) = htobe32(v))
+#define unaligned_write_htobe64(p, v) (*(uint64_t*)(p) = htobe64(v))
+
+#else
+
+#define unaligned_read_le16toh(p) ( \
+ (((uint16_t)(((uint8_t *)(p))[0])) << 0) | \
+ (((uint16_t)(((uint8_t *)(p))[1])) << 8))
+
+#define unaligned_read_le32toh(p) ( \
+ (((uint32_t)(((uint8_t *)(p))[0])) << 0) | \
+ (((uint32_t)(((uint8_t *)(p))[1])) << 8) | \
+ (((uint32_t)(((uint8_t *)(p))[2])) << 16) | \
+ (((uint32_t)(((uint8_t *)(p))[3])) << 24))
+
+#define unaligned_read_le64toh(p) ( \
+ (((uint64_t)(((uint8_t *)(p))[0])) << 0) | \
+ (((uint64_t)(((uint8_t *)(p))[1])) << 8) | \
+ (((uint64_t)(((uint8_t *)(p))[2])) << 16) | \
+ (((uint64_t)(((uint8_t *)(p))[3])) << 24) | \
+ (((uint64_t)(((uint8_t *)(p))[4])) << 32) | \
+ (((uint64_t)(((uint8_t *)(p))[5])) << 40) | \
+ (((uint64_t)(((uint8_t *)(p))[6])) << 48) | \
+ (((uint64_t)(((uint8_t *)(p))[7])) << 56))
+
+#define unaligned_read_be16toh(p) ( \
+ (((uint16_t)(((uint8_t *)(p))[0])) << 8) | \
+ (((uint16_t)(((uint8_t *)(p))[1])) << 0))
+
+#define unaligned_read_be32toh(p) ( \
+ (((uint32_t)(((uint8_t *)(p))[0])) << 24) | \
+ (((uint32_t)(((uint8_t *)(p))[1])) << 16) | \
+ (((uint32_t)(((uint8_t *)(p))[2])) << 8) | \
+ (((uint32_t)(((uint8_t *)(p))[3])) << 0))
+
+#define unaligned_read_be64toh(p) ( \
+ (((uint64_t)(((uint8_t *)(p))[0])) << 56) | \
+ (((uint64_t)(((uint8_t *)(p))[1])) << 48) | \
+ (((uint64_t)(((uint8_t *)(p))[2])) << 40) | \
+ (((uint64_t)(((uint8_t *)(p))[3])) << 32) | \
+ (((uint64_t)(((uint8_t *)(p))[4])) << 24) | \
+ (((uint64_t)(((uint8_t *)(p))[5])) << 16) | \
+ (((uint64_t)(((uint8_t *)(p))[6])) << 8) | \
+ (((uint64_t)(((uint8_t *)(p))[7])) << 0))
+
+#define unaligned_write_htole16(p, v) do { \
+ ((uint8_t *)(p))[0] = (uint8_t)(((uint16_t)(v)) >> 0); \
+ ((uint8_t *)(p))[1] = (uint8_t)(((uint16_t)(v)) >> 8); \
+ } while (0)
+
+#define unaligned_write_htole32(p, v) do { \
+ ((uint8_t *)(p))[0] = (uint8_t)(((uint32_t)(v)) >> 0); \
+ ((uint8_t *)(p))[1] = (uint8_t)(((uint32_t)(v)) >> 8); \
+ ((uint8_t *)(p))[2] = (uint8_t)(((uint32_t)(v)) >> 16); \
+ ((uint8_t *)(p))[3] = (uint8_t)(((uint32_t)(v)) >> 24); \
+ } while (0)
+
+#define unaligned_write_htole64(p) do { \
+ ((uint8_t *)(p))[0] = (uint8_t)(((uint64_t)(v)) >> 0); \
+ ((uint8_t *)(p))[1] = (uint8_t)(((uint64_t)(v)) >> 8); \
+ ((uint8_t *)(p))[2] = (uint8_t)(((uint64_t)(v)) >> 16); \
+ ((uint8_t *)(p))[3] = (uint8_t)(((uint64_t)(v)) >> 24); \
+ ((uint8_t *)(p))[4] = (uint8_t)(((uint64_t)(v)) >> 32); \
+ ((uint8_t *)(p))[5] = (uint8_t)(((uint64_t)(v)) >> 40); \
+ ((uint8_t *)(p))[6] = (uint8_t)(((uint64_t)(v)) >> 48); \
+ ((uint8_t *)(p))[7] = (uint8_t)(((uint64_t)(v)) >> 56); \
+ } while (0)
+
+#define unaligned_write_htobe16(p, v) do { \
+ ((uint8_t *)(p))[0] = (uint8_t)(((uint16_t)(v)) >> 8); \
+ ((uint8_t *)(p))[1] = (uint8_t)(((uint16_t)(v)) >> 0); \
+ } while (0)
+
+#define unaligned_write_htobe32(p, v) do { \
+ ((uint8_t *)(p))[0] = (uint8_t)(((uint32_t)(v)) >> 24); \
+ ((uint8_t *)(p))[1] = (uint8_t)(((uint32_t)(v)) >> 16); \
+ ((uint8_t *)(p))[2] = (uint8_t)(((uint32_t)(v)) >> 8); \
+ ((uint8_t *)(p))[3] = (uint8_t)(((uint32_t)(v)) >> 0); \
+ } while (0)
+
+#define unaligned_write_htobe64(p) do { \
+ ((uint8_t *)(p))[0] = (uint8_t)(((uint64_t)(v)) >> 56); \
+ ((uint8_t *)(p))[1] = (uint8_t)(((uint64_t)(v)) >> 48); \
+ ((uint8_t *)(p))[2] = (uint8_t)(((uint64_t)(v)) >> 40); \
+ ((uint8_t *)(p))[3] = (uint8_t)(((uint64_t)(v)) >> 32); \
+ ((uint8_t *)(p))[4] = (uint8_t)(((uint64_t)(v)) >> 24); \
+ ((uint8_t *)(p))[5] = (uint8_t)(((uint64_t)(v)) >> 16); \
+ ((uint8_t *)(p))[6] = (uint8_t)(((uint64_t)(v)) >> 8); \
+ ((uint8_t *)(p))[7] = (uint8_t)(((uint64_t)(v)) >> 0); \
+ } while (0)
+
+#if __LITTLE_ENDIAN__
+#define unaligned_read_16(p) unaligned_read_le16toh(p)
+#define unaligned_read_32(p) unaligned_read_le32toh(p)
+#define unaligned_read_64(p) unaligned_read_le64toh(p)
+
+#define unaligned_write_16(p) unaligned_write_htole16(p)
+#define unaligned_write_32(p) unaligned_write_htole32(p)
+#define unaligned_write_64(p) unaligned_write_htole64(p)
+#endif
+
+#if __BIG_ENDIAN__
+#define unaligned_read_16(p) unaligned_read_be16toh(p)
+#define unaligned_read_32(p) unaligned_read_be32toh(p)
+#define unaligned_read_64(p) unaligned_read_be64toh(p)
+
+#define unaligned_write_16(p) unaligned_write_htobe16(p)
+#define unaligned_write_32(p) unaligned_write_htobe32(p)
+#define unaligned_write_64(p) unaligned_write_htobe64(p)
+#endif
+
+#endif
+
+#endif
+
+#endif /* PUNALIGNED_H */
diff --git a/Source/library/FBSUtil/flatcc/portable/pversion.h b/Source/library/FBSUtil/flatcc/portable/pversion.h
new file mode 100644
index 0000000..48508cb
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pversion.h
@@ -0,0 +1,6 @@
+#define PORTABLE_VERSION_TEXT "0.2.2"
+#define PORTABLE_VERSION_MAJOR 0
+#define PORTABLE_VERSION_MINOR 2
+#define PORTABLE_VERSION_PATCH 2
+/* 1 or 0 */
+#define PORTABLE_VERSION_RELEASED 1
diff --git a/Source/library/FBSUtil/flatcc/portable/pwarnings.h b/Source/library/FBSUtil/flatcc/portable/pwarnings.h
new file mode 100644
index 0000000..8088712
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/portable/pwarnings.h
@@ -0,0 +1,44 @@
+#ifndef PWARNINGS_H
+#define PWARNINGS_H
+
+/*
+ * See also pdiagnostics.h headers for per file control of common
+ * warnings.
+ *
+ * This file is intended for global disabling of warnings that shouldn't
+ * be present in C11 or perhaps C99, or a generally just noise where
+ * recent clang / gcc compile cleanly with high warning levels.
+ */
+
+#if defined(_MSC_VER)
+/* Needed when flagging code in or out and more. */
+#pragma warning(disable: 4127) /* conditional expression is constant */
+/* happens also in MS's own headers. */
+#pragma warning(disable: 4668) /* preprocessor name not defined */
+/* MSVC does not respect double parenthesis for intent */
+#pragma warning(disable: 4706) /* assignment within conditional expression */
+/* `inline` only advisory anyway. */
+#pragma warning(disable: 4710) /* function not inlined */
+/* Well, we don't intend to add the padding manually. */
+#pragma warning(disable: 4820) /* x bytes padding added in struct */
+
+/*
+ * Don't warn that fopen etc. are unsafe
+ *
+ * Define a compiler flag like `-D_CRT_SECURE_NO_WARNINGS` in the build.
+ * For some reason it doesn't work when defined here.
+ *
+ * #define _CRT_SECURE_NO_WARNINGS
+ */
+
+/*
+ * Anonymous union in struct is valid in C11 and has been supported in
+ * GCC and Clang for a while, but it is not C99. MSVC also handles it,
+ * but warns. Truly portable code should perhaps not use this feature,
+ * but this is not the place to complain about it.
+ */
+#pragma warning(disable: 4201) /* nonstandard extension used: nameless struct/union */
+
+#endif /* _MSV_VER */
+
+#endif /* PWARNINGS_H */
diff --git a/Source/library/FBSUtil/flatcc/reflection/README.md b/Source/library/FBSUtil/flatcc/reflection/README.md
new file mode 100644
index 0000000..29bc096
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/reflection/README.md
@@ -0,0 +1,3 @@
+Generated by flatcc
+
+Keep checked in - needed by flatcc to generate binary schema.
diff --git a/Source/library/FBSUtil/flatcc/reflection/flatbuffers_common_builder.h b/Source/library/FBSUtil/flatcc/reflection/flatbuffers_common_builder.h
new file mode 100644
index 0000000..fefd7dd
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/reflection/flatbuffers_common_builder.h
@@ -0,0 +1,467 @@
+#ifndef FLATBUFFERS_COMMON_BUILDER_H
+#define FLATBUFFERS_COMMON_BUILDER_H
+
+/* Generated by flatcc 0.4.2 FlatBuffers schema compiler for C by dvide.com */
+
+/* Common FlatBuffers build functionality for C. */
+
+#define PDIAGNOSTIC_IGNORE_UNUSED
+#include "flatcc/portable/pdiagnostic_push.h"
+#ifndef FLATBUILDER_H
+#include "flatcc/flatcc_builder.h"
+#endif
+typedef flatcc_builder_t flatbuffers_builder_t;
+typedef flatcc_builder_ref_t flatbuffers_ref_t;
+typedef flatcc_builder_ref_t flatbuffers_vec_ref_t;
+/* integer return code (ref and ptr always fail on 0) */
+#define flatbuffers_failed(x) ((x) < 0)
+typedef flatbuffers_ref_t flatbuffers_root_t;
+#define flatbuffers_root(ref) ((flatbuffers_root_t)(ref))
+
+#define __flatbuffers_build_buffer(NS)\
+typedef NS ## ref_t NS ## buffer_ref_t;\
+static inline int NS ## buffer_start(NS ## builder_t *B, NS ##fid_t fid)\
+{ return flatcc_builder_start_buffer(B, fid, 0, 0); }\
+static inline int NS ## buffer_start_with_size(NS ## builder_t *B, NS ##fid_t fid)\
+{ return flatcc_builder_start_buffer(B, fid, 0, flatcc_builder_with_size); }\
+static inline int NS ## buffer_start_aligned(NS ## builder_t *B, NS ##fid_t fid, uint16_t block_align)\
+{ return flatcc_builder_start_buffer(B, fid, block_align, 0); }\
+static inline int NS ## buffer_start_aligned_with_size(NS ## builder_t *B, NS ##fid_t fid, uint16_t block_align)\
+{ return flatcc_builder_start_buffer(B, fid, block_align, flatcc_builder_with_size); }\
+static inline NS ## buffer_ref_t NS ## buffer_end(NS ## builder_t *B, NS ## ref_t root)\
+{ return flatcc_builder_end_buffer(B, root); }
+
+#define __flatbuffers_build_table_root(NS, N, FID, TFID)\
+static inline int N ## _start_as_root(NS ## builder_t *B)\
+{ return NS ## buffer_start(B, FID) ? -1 : N ## _start(B); }\
+static inline int N ## _start_as_root_with_size(NS ## builder_t *B)\
+{ return NS ## buffer_start_with_size(B, FID) ? -1 : N ## _start(B); }\
+static inline int N ## _start_as_typed_root(NS ## builder_t *B)\
+{ return NS ## buffer_start(B, TFID) ? -1 : N ## _start(B); }\
+static inline int N ## _start_as_typed_root_with_size(NS ## builder_t *B)\
+{ return NS ## buffer_start_with_size(B, TFID) ? -1 : N ## _start(B); }\
+static inline NS ## buffer_ref_t N ## _end_as_root(NS ## builder_t *B)\
+{ return NS ## buffer_end(B, N ## _end(B)); }\
+static inline NS ## buffer_ref_t N ## _end_as_typed_root(NS ## builder_t *B)\
+{ return NS ## buffer_end(B, N ## _end(B)); }\
+static inline NS ## buffer_ref_t N ## _create_as_root(NS ## builder_t *B __ ## N ## _formal_args)\
+{ if (NS ## buffer_start(B, FID)) return 0; return NS ## buffer_end(B, N ## _create(B __ ## N ## _call_args)); }\
+static inline NS ## buffer_ref_t N ## _create_as_root_with_size(NS ## builder_t *B __ ## N ## _formal_args)\
+{ if (NS ## buffer_start_with_size(B, FID)) return 0; return NS ## buffer_end(B, N ## _create(B __ ## N ## _call_args)); }\
+static inline NS ## buffer_ref_t N ## _create_as_typed_root(NS ## builder_t *B __ ## N ## _formal_args)\
+{ if (NS ## buffer_start(B, TFID)) return 0; return NS ## buffer_end(B, N ## _create(B __ ## N ## _call_args)); }\
+static inline NS ## buffer_ref_t N ## _create_as_typed_root_with_size(NS ## builder_t *B __ ## N ## _formal_args)\
+{ if (NS ## buffer_start_with_size(B, TFID)) return 0; return NS ## buffer_end(B, N ## _create(B __ ## N ## _call_args)); }
+
+#define __flatbuffers_build_table_prolog(NS, N, FID, TFID)\
+__flatbuffers_build_table_vector_ops(NS, N ## _vec, N)\
+__flatbuffers_build_table_root(NS, N, FID, TFID)
+
+#define __flatbuffers_build_struct_root(NS, N, A, FID, TFID)\
+static inline N ## _t *N ## _start_as_root(NS ## builder_t *B)\
+{ return NS ## buffer_start(B, FID) ? 0 : N ## _start(B); }\
+static inline N ## _t *N ## _start_as_root_with_size(NS ## builder_t *B)\
+{ return NS ## buffer_start_with_size(B, FID) ? 0 : N ## _start(B); }\
+static inline N ## _t *N ## _start_as_typed_root(NS ## builder_t *B)\
+{ return NS ## buffer_start(B, TFID) ? 0 : N ## _start(B); }\
+static inline N ## _t *N ## _start_as_typed_root_with_size(NS ## builder_t *B)\
+{ return NS ## buffer_start_with_size(B, TFID) ? 0 : N ## _start(B); }\
+static inline NS ## buffer_ref_t N ## _end_as_root(NS ## builder_t *B)\
+{ return NS ## buffer_end(B, N ## _end(B)); }\
+static inline NS ## buffer_ref_t N ## _end_as_typed_root(NS ## builder_t *B)\
+{ return NS ## buffer_end(B, N ## _end(B)); }\
+static inline NS ## buffer_ref_t N ## _end_pe_as_root(NS ## builder_t *B)\
+{ return NS ## buffer_end(B, N ## _end_pe(B)); }\
+static inline NS ## buffer_ref_t N ## _end_pe_as_typed_root(NS ## builder_t *B)\
+{ return NS ## buffer_end(B, N ## _end_pe(B)); }\
+static inline NS ## buffer_ref_t N ## _create_as_root(NS ## builder_t *B __ ## N ## _formal_args)\
+{ return flatcc_builder_create_buffer(B, FID, 0,\
+ N ## _create(B __ ## N ## _call_args), A, 0); }\
+static inline NS ## buffer_ref_t N ## _create_as_root_with_size(NS ## builder_t *B __ ## N ## _formal_args)\
+{ return flatcc_builder_create_buffer(B, FID, 0,\
+ N ## _create(B __ ## N ## _call_args), A, flatcc_builder_with_size); }\
+static inline NS ## buffer_ref_t N ## _create_as_typed_root(NS ## builder_t *B __ ## N ## _formal_args)\
+{ return flatcc_builder_create_buffer(B, TFID, 0,\
+ N ## _create(B __ ## N ## _call_args), A, 0); }\
+static inline NS ## buffer_ref_t N ## _create_as_typed_root_with_size(NS ## builder_t *B __ ## N ## _formal_args)\
+{ return flatcc_builder_create_buffer(B, TFID, 0,\
+ N ## _create(B __ ## N ## _call_args), A, flatcc_builder_with_size); }
+
+#define __flatbuffers_build_nested_table_root(NS, N, TN, FID, TFID)\
+static inline int N ## _start_as_root(NS ## builder_t *B)\
+{ return NS ## buffer_start(B, FID) ? -1 : TN ## _start(B); }\
+static inline int N ## _start_as_typed_root(NS ## builder_t *B)\
+{ return NS ## buffer_start(B, TFID) ? -1 : TN ## _start(B); }\
+static inline int N ## _end_as_root(NS ## builder_t *B)\
+{ return N ## _add(B, NS ## buffer_end(B, TN ## _end(B))); }\
+static inline int N ## _end_as_typed_root(NS ## builder_t *B)\
+{ return N ## _add(B, NS ## buffer_end(B, TN ## _end(B))); }\
+static inline int N ## _nest(NS ## builder_t *B, void *data, size_t size, uint16_t align)\
+{ if (NS ## buffer_start(B, FID)) return -1;\
+ return N ## _add(B, NS ## buffer_end(B, flatcc_builder_create_vector(B, data, size, 1,\
+ align ? align : 8, FLATBUFFERS_COUNT_MAX(1)))); }\
+static inline int N ## _typed_nest(NS ## builder_t *B, void *data, size_t size, uint16_t align)\
+{ if (NS ## buffer_start(B, TFID)) return -1;\
+ return N ## _add(B, NS ## buffer_end(B, flatcc_builder_create_vector(B, data, size, 1,\
+ align ? align : 8, FLATBUFFERS_COUNT_MAX(1)))); }
+
+#define __flatbuffers_build_nested_struct_root(NS, N, TN, A, FID, TFID)\
+static inline TN ## _t *N ## _start_as_root(NS ## builder_t *B)\
+{ return NS ## buffer_start(B, FID) ? 0 : TN ## _start(B); }\
+static inline TN ## _t *N ## _start_as_typed_root(NS ## builder_t *B)\
+{ return NS ## buffer_start(B, FID) ? 0 : TN ## _start(B); }\
+static inline int N ## _end_as_root(NS ## builder_t *B)\
+{ return N ## _add(B, NS ## buffer_end(B, TN ## _end(B))); }\
+static inline int N ## _end_as_typed_root(NS ## builder_t *B)\
+{ return N ## _add(B, NS ## buffer_end(B, TN ## _end(B))); }\
+static inline int N ## _end_pe_as_root(NS ## builder_t *B)\
+{ return N ## _add(B, NS ## buffer_end(B, TN ## _end_pe(B))); }\
+static inline int N ## _create_as_root(NS ## builder_t *B __ ## TN ## _formal_args)\
+{ return N ## _add(B, flatcc_builder_create_buffer(B, FID, 0,\
+ TN ## _create(B __ ## TN ## _call_args), A, flatcc_builder_is_nested)); }\
+static inline int N ## _create_as_typed_root(NS ## builder_t *B __ ## TN ## _formal_args)\
+{ return N ## _add(B, flatcc_builder_create_buffer(B, TFID, 0,\
+ TN ## _create(B __ ## TN ## _call_args), A, flatcc_builder_is_nested)); }\
+static inline int N ## _nest(NS ## builder_t *B, void *data, size_t size, uint16_t align)\
+{ if (NS ## buffer_start(B, FID)) return -1;\
+ return N ## _add(B, NS ## buffer_end(B, flatcc_builder_create_vector(B, data, size, 1,\
+ align < A ? A : align, FLATBUFFERS_COUNT_MAX(1)))); }\
+static inline int N ## _typed_nest(NS ## builder_t *B, void *data, size_t size, uint16_t align)\
+{ if (NS ## buffer_start(B, TFID)) return -1;\
+ return N ## _add(B, NS ## buffer_end(B, flatcc_builder_create_vector(B, data, size, 1,\
+ align < A ? A : align, FLATBUFFERS_COUNT_MAX(1)))); }
+
+#define __flatbuffers_build_vector_ops(NS, V, N, TN, T)\
+static inline T *V ## _extend(NS ## builder_t *B, size_t len)\
+{ return flatcc_builder_extend_vector(B, len); }\
+static inline T *V ## _append(NS ## builder_t *B, const T *data, size_t len)\
+{ return flatcc_builder_append_vector(B, data, len); }\
+static inline int V ## _truncate(NS ## builder_t *B, size_t len)\
+{ return flatcc_builder_truncate_vector(B, len); }\
+static inline T *V ## _edit(NS ## builder_t *B)\
+{ return flatcc_builder_vector_edit(B); }\
+static inline size_t V ## _reserved_len(NS ## builder_t *B)\
+{ return flatcc_builder_vector_count(B); }\
+static inline T *V ## _push(NS ## builder_t *B, const T *p)\
+{ T *_p; return (_p = flatcc_builder_extend_vector(B, 1)) ? (memcpy(_p, p, TN ## __size()), _p) : 0; }\
+static inline T *V ## _push_copy(NS ## builder_t *B, const T *p)\
+{ T *_p; return (_p = flatcc_builder_extend_vector(B, 1)) ? TN ## _copy(_p, p) : 0; }\
+static inline T *V ## _push_create(NS ## builder_t *B __ ## TN ## _formal_args)\
+{ T *_p; return (_p = flatcc_builder_extend_vector(B, 1)) ? TN ## _assign(_p __ ## TN ## _call_args) : 0; }
+
+#define __flatbuffers_build_vector(NS, N, T, S, A)\
+typedef NS ## ref_t N ## _vec_ref_t;\
+static inline int N ## _vec_start(NS ## builder_t *B)\
+{ return flatcc_builder_start_vector(B, S, A, FLATBUFFERS_COUNT_MAX(S)); }\
+static inline N ## _vec_ref_t N ## _vec_end_pe(NS ## builder_t *B)\
+{ return flatcc_builder_end_vector(B); }\
+static inline N ## _vec_ref_t N ## _vec_end(NS ## builder_t *B)\
+{ if (!NS ## is_native_pe()) { size_t i, n; T *p = flatcc_builder_vector_edit(B);\
+ for (i = 0, n = flatcc_builder_vector_count(B); i < n; ++i)\
+ { N ## _to_pe(N ## __ptr_add(p, i)); }} return flatcc_builder_end_vector(B); }\
+static inline N ## _vec_ref_t N ## _vec_create_pe(NS ## builder_t *B, const T *data, size_t len)\
+{ return flatcc_builder_create_vector(B, data, len, S, A, FLATBUFFERS_COUNT_MAX(S)); }\
+static inline N ## _vec_ref_t N ## _vec_create(NS ## builder_t *B, const T *data, size_t len)\
+{ if (!NS ## is_native_pe()) { size_t i; T *p; int ret = flatcc_builder_start_vector(B, S, A, FLATBUFFERS_COUNT_MAX(S)); if (ret) { return ret; }\
+ p = flatcc_builder_extend_vector(B, len); if (!p) return 0;\
+ for (i = 0; i < len; ++i) { N ## _copy_to_pe(N ## __ptr_add(p, i), N ## __const_ptr_add(data, i)); }\
+ return flatcc_builder_end_vector(B); } else return flatcc_builder_create_vector(B, data, len, S, A, FLATBUFFERS_COUNT_MAX(S)); }\
+static inline N ## _vec_ref_t N ## _vec_clone(NS ## builder_t *B, N ##_vec_t vec)\
+{ return flatcc_builder_create_vector(B, vec, N ## _vec_len(vec), S, A, FLATBUFFERS_COUNT_MAX(S)); }\
+static inline N ## _vec_ref_t N ## _vec_slice(NS ## builder_t *B, N ##_vec_t vec, size_t index, size_t len)\
+{ size_t n = N ## _vec_len(vec); if (index >= n) index = n; n -= index; if (len > n) len = n;\
+ return flatcc_builder_create_vector(B, N ## __const_ptr_add(vec, index), len, S, A, FLATBUFFERS_COUNT_MAX(S)); }\
+__flatbuffers_build_vector_ops(NS, N ## _vec, N, N, T)
+
+#define __flatbuffers_build_string_vector_ops(NS, N)\
+static inline int N ## _push_start(NS ## builder_t *B)\
+{ return NS ## string_start(B); }\
+static inline NS ## string_ref_t *N ## _push_end(NS ## builder_t *B)\
+{ return NS ## string_vec_push(B, NS ## string_end(B)); }\
+static inline NS ## string_ref_t *N ## _push_create(NS ## builder_t *B, const char *s, size_t len)\
+{ return NS ## string_vec_push(B, NS ## string_create(B, s, len)); }\
+static inline NS ## string_ref_t *N ## _push_create_str(NS ## builder_t *B, const char *s)\
+{ return NS ## string_vec_push(B, NS ## string_create_str(B, s)); }\
+static inline NS ## string_ref_t *N ## _push_create_strn(NS ## builder_t *B, const char *s, size_t max_len)\
+{ return NS ## string_vec_push(B, NS ## string_create_strn(B, s, max_len)); }\
+static inline NS ## string_ref_t *N ## _push_clone(NS ## builder_t *B, NS ## string_t string)\
+{ return NS ## string_vec_push(B, NS ## string_clone(B, string)); }\
+static inline NS ## string_ref_t *N ## _push_slice(NS ## builder_t *B, NS ## string_t string, size_t index, size_t len)\
+{ return NS ## string_vec_push(B, NS ## string_slice(B, string, index, len)); }
+
+#define __flatbuffers_build_table_vector_ops(NS, N, TN)\
+static inline int N ## _push_start(NS ## builder_t *B)\
+{ return TN ## _start(B); }\
+static inline TN ## _ref_t *N ## _push_end(NS ## builder_t *B)\
+{ return N ## _push(B, TN ## _end(B)); }\
+static inline TN ## _ref_t *N ## _push_create(NS ## builder_t *B __ ## TN ##_formal_args)\
+{ return N ## _push(B, TN ## _create(B __ ## TN ## _call_args)); }
+
+#define __flatbuffers_build_offset_vector_ops(NS, V, N, TN)\
+static inline TN ## _ref_t *V ## _extend(NS ## builder_t *B, size_t len)\
+{ return flatcc_builder_extend_offset_vector(B, len); }\
+static inline TN ## _ref_t *V ## _append(NS ## builder_t *B, const TN ## _ref_t *data, size_t len)\
+{ return flatcc_builder_append_offset_vector(B, data, len); }\
+static inline int V ## _truncate(NS ## builder_t *B, size_t len)\
+{ return flatcc_builder_truncate_offset_vector(B, len); }\
+static inline TN ## _ref_t *V ## _edit(NS ## builder_t *B)\
+{ return flatcc_builder_offset_vector_edit(B); }\
+static inline size_t V ## _reserved_len(NS ## builder_t *B)\
+{ return flatcc_builder_offset_vector_count(B); }\
+static inline TN ## _ref_t *V ## _push(NS ## builder_t *B, const TN ## _ref_t ref)\
+{ return ref ? flatcc_builder_offset_vector_push(B, ref) : 0; }
+
+#define __flatbuffers_build_offset_vector(NS, N)\
+typedef NS ## ref_t N ## _vec_ref_t;\
+static inline int N ## _vec_start(NS ## builder_t *B)\
+{ return flatcc_builder_start_offset_vector(B); }\
+static inline N ## _vec_ref_t N ## _vec_end(NS ## builder_t *B)\
+{ return flatcc_builder_end_offset_vector(B); }\
+static inline N ## _vec_ref_t N ## _vec_create(NS ## builder_t *B, const N ## _ref_t *data, size_t len)\
+{ return flatcc_builder_create_offset_vector(B, data, len); }\
+__flatbuffers_build_offset_vector_ops(NS, N ## _vec, N, N)
+
+#define __flatbuffers_build_string_ops(NS, N)\
+static inline char *N ## _append(NS ## builder_t *B, const char *s, size_t len)\
+{ return flatcc_builder_append_string(B, s, len); }\
+static inline char *N ## _append_str(NS ## builder_t *B, const char *s)\
+{ return flatcc_builder_append_string_str(B, s); }\
+static inline char *N ## _append_strn(NS ## builder_t *B, const char *s, size_t len)\
+{ return flatcc_builder_append_string_strn(B, s, len); }\
+static inline size_t N ## _reserved_len(NS ## builder_t *B)\
+{ return flatcc_builder_string_len(B); }\
+static inline char *N ## _extend(NS ## builder_t *B, size_t len)\
+{ return flatcc_builder_extend_string(B, len); }\
+static inline char *N ## _edit(NS ## builder_t *B)\
+{ return flatcc_builder_string_edit(B); }\
+static inline int N ## _truncate(NS ## builder_t *B, size_t len)\
+{ return flatcc_builder_truncate_string(B, len); }
+
+#define __flatbuffers_build_string(NS)\
+typedef NS ## ref_t NS ## string_ref_t;\
+static inline int NS ## string_start(NS ## builder_t *B)\
+{ return flatcc_builder_start_string(B); }\
+static inline NS ## string_ref_t NS ## string_end(NS ## builder_t *B)\
+{ return flatcc_builder_end_string(B); }\
+static inline NS ## ref_t NS ## string_create(NS ## builder_t *B, const char *s, size_t len)\
+{ return flatcc_builder_create_string(B, s, len); }\
+static inline NS ## ref_t NS ## string_create_str(NS ## builder_t *B, const char *s)\
+{ return flatcc_builder_create_string_str(B, s); }\
+static inline NS ## ref_t NS ## string_create_strn(NS ## builder_t *B, const char *s, size_t len)\
+{ return flatcc_builder_create_string_strn(B, s, len); }\
+static inline NS ## string_ref_t NS ## string_clone(NS ## builder_t *B, NS ## string_t string)\
+{ return flatcc_builder_create_string(B, string, NS ## string_len(string)); }\
+static inline NS ## string_ref_t NS ## string_slice(NS ## builder_t *B, NS ## string_t string, size_t index, size_t len)\
+{ size_t n = NS ## string_len(string); if (index >= n) index = n; n -= index; if (len > n) len = n;\
+ return flatcc_builder_create_string(B, string + index, len); }\
+__flatbuffers_build_string_ops(NS, NS ## string)\
+__flatbuffers_build_offset_vector(NS, NS ## string)
+
+#define __flatbuffers_copy_from_pe(P, P2, N) (*(P) = N ## _cast_from_pe(*P2), (P))
+#define __flatbuffers_from_pe(P, N) (*(P) = N ## _cast_from_pe(*P), (P))
+#define __flatbuffers_copy_to_pe(P, P2, N) (*(P) = N ## _cast_to_pe(*P2), (P))
+#define __flatbuffers_to_pe(P, N) (*(P) = N ## _cast_to_pe(*P), (P))
+#define __flatbuffers_define_scalar_primitives(NS, N, T)\
+static inline T *N ## _from_pe(T *p) { return __ ## NS ## from_pe(p, N); }\
+static inline T *N ## _to_pe(T *p) { return __ ## NS ## to_pe(p, N); }\
+static inline T *N ## _copy(T *p, const T *p2) { *p = *p2; return p; }\
+static inline T *N ## _copy_from_pe(T *p, const T *p2)\
+{ return __ ## NS ## copy_from_pe(p, p2, N); }\
+static inline T *N ## _copy_to_pe(T *p, const T *p2) \
+{ return __ ## NS ## copy_to_pe(p, p2, N); }\
+static inline T *N ## _assign(T *p, const T v0) { *p = v0; return p; }\
+static inline T *N ## _assign_from_pe(T *p, T v0)\
+{ *p = N ## _cast_from_pe(v0); return p; }\
+static inline T *N ## _assign_to_pe(T *p, T v0)\
+{ *p = N ## _cast_to_pe(v0); return p; }
+#define __flatbuffers_build_scalar(NS, N, T)\
+__ ## NS ## define_scalar_primitives(NS, N, T)\
+__ ## NS ## build_vector(NS, N, T, sizeof(T), sizeof(T))
+/* Depends on generated copy_to/from_pe functions, and the type. */
+#define __flatbuffers_define_struct_primitives(NS, N)\
+static inline N ## _t *N ##_to_pe(N ## _t *p)\
+{ if (!NS ## is_native_pe()) { N ## _copy_to_pe(p, p); }; return p; }\
+static inline N ## _t *N ##_from_pe(N ## _t *p)\
+{ if (!NS ## is_native_pe()) { N ## _copy_from_pe(p, p); }; return p; }\
+static inline N ## _t *N ## _clear(N ## _t *p) { return memset(p, 0, N ## __size()); }
+
+/* Depends on generated copy/assign_to/from_pe functions, and the type. */
+#define __flatbuffers_build_struct(NS, N, S, A, FID, TFID)\
+__ ## NS ## define_struct_primitives(NS, N)\
+typedef NS ## ref_t N ## _ref_t;\
+static inline N ## _t *N ## _start(NS ## builder_t *B)\
+{ return flatcc_builder_start_struct(B, S, A); }\
+static inline N ## _ref_t N ## _end(NS ## builder_t *B)\
+{ if (!NS ## is_native_pe()) { N ## _to_pe(flatcc_builder_struct_edit(B)); }\
+ return flatcc_builder_end_struct(B); }\
+static inline N ## _ref_t N ## _end_pe(NS ## builder_t *B)\
+{ return flatcc_builder_end_struct(B); }\
+static inline N ## _ref_t N ## _create(NS ## builder_t *B __ ## N ## _formal_args)\
+{ N ## _t *_p = N ## _start(B); if (!_p) return 0; N ##_assign_to_pe(_p __ ## N ## _call_args);\
+ return N ## _end_pe(B); }\
+__flatbuffers_build_vector(NS, N, N ## _t, S, A)\
+__flatbuffers_build_struct_root(NS, N, A, FID, TFID)
+
+#define __flatbuffers_build_table(NS, N, K)\
+typedef NS ## ref_t N ## _ref_t;\
+static inline int N ## _start(NS ## builder_t *B)\
+{ return flatcc_builder_start_table(B, K); }\
+static inline N ## _ref_t N ## _end(NS ## builder_t *B)\
+{ assert(flatcc_builder_check_required(B, __ ## N ## _required,\
+ sizeof(__ ## N ## _required) / sizeof(__ ## N ## _required[0]) - 1));\
+ return flatcc_builder_end_table(B); }\
+__flatbuffers_build_offset_vector(NS, N)
+
+#define __flatbuffers_build_table_field(ID, NS, N, TN)\
+static inline int N ## _add(NS ## builder_t *B, TN ## _ref_t ref)\
+{ TN ## _ref_t *_p; return (ref && (_p = flatcc_builder_table_add_offset(B, ID))) ?\
+ ((*_p = ref), 0) : -1; }\
+static inline int N ## _start(NS ## builder_t *B)\
+{ return TN ## _start(B); }\
+static inline int N ## _end(NS ## builder_t *B)\
+{ return N ## _add(B, TN ## _end(B)); }\
+static inline TN ## _ref_t N ## _create(NS ## builder_t *B __ ## TN ##_formal_args)\
+{ return N ## _add(B, TN ## _create(B __ ## TN ## _call_args)); }
+
+#define __flatbuffers_build_union_field(ID, NS, N, TN)\
+static inline int N ## _add(NS ## builder_t *B, TN ## _union_ref_t uref)\
+{ NS ## ref_t *_p; TN ## _union_type_t *_pt; if (uref.type == TN ## _NONE) return 0; if (uref._member == 0) return -1;\
+ if (!(_pt = flatcc_builder_table_add(B, ID - 1, sizeof(*_pt), sizeof(_pt))) ||\
+ !(_p = flatcc_builder_table_add_offset(B, ID))) return -1; *_pt = uref.type; *_p = uref._member; return 0; }\
+static inline int N ## _add_type(NS ## builder_t *B, TN ## _union_type_t type)\
+{ TN ## _union_type_t *_pt; if (type == TN ## _NONE) return 0; return (_pt = flatcc_builder_table_add(B, ID - 1,\
+ sizeof(*_pt), sizeof(*_pt))) ? ((*_pt = type), 0) : -1; }\
+static inline int N ## _add_member(NS ## builder_t *B, TN ## _union_ref_t uref)\
+{ NS ## ref_t *p; if (uref.type == TN ## _NONE) return 0; return (p = flatcc_builder_table_add_offset(B, ID)) ?\
+ ((*p = uref._member), 0) : -1; }
+
+/* M is the union member name and T is its type, i.e. the qualified name. */
+#define __flatbuffers_build_union_member_field(NS, N, NU, M, T)\
+static inline int N ## _ ## M ## _add(NS ## builder_t *B, T ## _ref_t ref)\
+{ return N ## _add(B, NU ## _as_ ## M (ref)); }\
+static inline int N ## _ ## M ## _start(NS ## builder_t *B)\
+{ return T ## _start(B); }\
+static inline int N ## _ ## M ## _end(NS ## builder_t *B)\
+{ return N ## _ ## M ## _add(B, T ## _end(B)); }
+
+/* NS: common namespace, ID: table field id (not offset), TN: name of type T,
+ * S: sizeof of scalar type, A: alignment of type T, default value V of type T. */
+#define __flatbuffers_build_scalar_field(ID, NS, N, TN, T, S, A, V)\
+static inline int N ## _add(NS ## builder_t *B, const T v)\
+{ T *_p; if (v == V) return 0; if (!(_p = flatcc_builder_table_add(B, ID, S, A))) return -1;\
+ TN ## _assign_to_pe(_p, v); return 0; }\
+static inline int N ## _force_add(NS ## builder_t *B, const T v)\
+{ T *_p; if (!(_p = flatcc_builder_table_add(B, ID, S, A))) return -1;\
+ TN ## _assign_to_pe(_p, v); return 0; }\
+
+#define __flatbuffers_build_struct_field(ID, NS, N, TN, S, A)\
+static inline TN ## _t *N ## _start(NS ## builder_t *B)\
+{ return flatcc_builder_table_add(B, ID, S, A); }\
+static inline int N ## _end(NS ## builder_t *B)\
+{ if (!NS ## is_native_pe()) { TN ## _to_pe(flatcc_builder_table_edit(B, S)); } return 0; }\
+static inline int N ## _end_pe(NS ## builder_t *B) { return 0; }\
+static inline int N ## _create(NS ## builder_t *B __ ## TN ## _formal_args)\
+{ TN ## _t *_p = N ## _start(B); if (!_p) return 0; TN ##_assign_to_pe(_p __ ## TN ## _call_args);\
+ return 0; }\
+static inline int N ## _add(NS ## builder_t *B, const TN ## _t *p)\
+{ TN ## _t *_p = N ## _start(B); if (!_p) return -1; TN ##_copy_to_pe(_p, p); return 0; }\
+static inline int N ## _clone(NS ## builder_t *B, TN ## _struct_t p)\
+{ return 0 == flatcc_builder_table_add_copy(B, ID, p, S, A) ? -1 : 0; }
+
+#define __flatbuffers_build_vector_field(ID, NS, N, TN, T)\
+static inline int N ## _add(NS ## builder_t *B, TN ## _vec_ref_t ref)\
+{ TN ## _vec_ref_t *_p; return (ref && (_p = flatcc_builder_table_add_offset(B, ID))) ? ((*_p = ref), 0) : -1; }\
+static inline int N ## _start(NS ## builder_t *B)\
+{ return TN ## _vec_start(B); }\
+static inline int N ## _end_pe(NS ## builder_t *B)\
+{ return N ## _add(B, TN ## _vec_end_pe(B)); }\
+static inline int N ## _end(NS ## builder_t *B)\
+{ return N ## _add(B, TN ## _vec_end(B)); }\
+static inline int N ## _create_pe(NS ## builder_t *B, T *data, size_t len)\
+{ return N ## _add(B, TN ## _vec_create_pe(B, data, len)); }\
+static inline int N ## _create(NS ## builder_t *B, T *data, size_t len)\
+{ return N ## _add(B, TN ## _vec_create(B, data, len)); }\
+static inline int N ## _clone(NS ## builder_t *B, TN ## _vec_t vec)\
+{ return N ## _add(B, TN ## _vec_clone(B, vec)); }\
+static inline int N ## _slice(NS ## builder_t *B, TN ## _vec_t vec, size_t index, size_t len)\
+{ return N ## _add(B, TN ## _vec_slice(B, vec, index, len)); }\
+__flatbuffers_build_vector_ops(NS, N, N, TN, T)
+
+#define __flatbuffers_build_offset_vector_field(ID, NS, N, TN)\
+static inline int N ## _add(NS ## builder_t *B, TN ## _vec_ref_t ref)\
+{ TN ## _vec_ref_t *_p; return (ref && (_p = flatcc_builder_table_add_offset(B, ID))) ? ((*_p = ref), 0) : -1; }\
+static inline int N ## _start(NS ## builder_t *B)\
+{ return flatcc_builder_start_offset_vector(B); }\
+static inline int N ## _end(NS ## builder_t *B)\
+{ return N ## _add(B, flatcc_builder_end_offset_vector(B)); }\
+static inline int N ## _create(NS ## builder_t *B, const TN ## _ref_t *data, size_t len)\
+{ return N ## _add(B, flatcc_builder_create_offset_vector(B, data, len)); }\
+__flatbuffers_build_offset_vector_ops(NS, N, N, TN)
+
+#define __flatbuffers_build_string_field(ID, NS, N)\
+static inline int N ## _add(NS ## builder_t *B, NS ## string_ref_t ref)\
+{ NS ## string_ref_t *_p; return (ref && (_p = flatcc_builder_table_add_offset(B, ID))) ? ((*_p = ref), 0) : -1; }\
+static inline int N ## _start(NS ## builder_t *B)\
+{ return flatcc_builder_start_string(B); }\
+static inline int N ## _end(NS ## builder_t *B)\
+{ return N ## _add(B, flatcc_builder_end_string(B)); }\
+static inline int N ## _create(NS ## builder_t *B, const char *s, size_t len)\
+{ return N ## _add(B, flatcc_builder_create_string(B, s, len)); }\
+static inline int N ## _create_str(NS ## builder_t *B, const char *s)\
+{ return N ## _add(B, flatcc_builder_create_string_str(B, s)); }\
+static inline int N ## _create_strn(NS ## builder_t *B, const char *s, size_t max_len)\
+{ return N ## _add(B, flatcc_builder_create_string_strn(B, s, max_len)); }\
+static inline int N ## _clone(NS ## builder_t *B, NS ## string_t string)\
+{ return N ## _add(B, NS ## string_clone(B, string)); }\
+static inline int N ## _slice(NS ## builder_t *B, NS ## string_t string, size_t index, size_t len)\
+{ return N ## _add(B, NS ## string_slice(B, string, index, len)); }\
+__flatbuffers_build_string_ops(NS, N)
+
+#define __flatbuffers_build_table_vector_field(ID, NS, N, TN)\
+__flatbuffers_build_offset_vector_field(ID, NS, N, TN)\
+__flatbuffers_build_table_vector_ops(NS, N, TN)
+
+#define __flatbuffers_build_string_vector_field(ID, NS, N)\
+__flatbuffers_build_offset_vector_field(ID, NS, N, NS ## string)\
+__flatbuffers_build_string_vector_ops(NS, N)
+
+#define __flatbuffers_uint8_formal_args , uint8_t v0
+#define __flatbuffers_uint8_call_args , v0
+#define __flatbuffers_int8_formal_args , int8_t v0
+#define __flatbuffers_int8_call_args , v0
+#define __flatbuffers_bool_formal_args , flatbuffers_bool_t v0
+#define __flatbuffers_bool_call_args , v0
+#define __flatbuffers_uint16_formal_args , uint16_t v0
+#define __flatbuffers_uint16_call_args , v0
+#define __flatbuffers_uint32_formal_args , uint32_t v0
+#define __flatbuffers_uint32_call_args , v0
+#define __flatbuffers_uint64_formal_args , uint64_t v0
+#define __flatbuffers_uint64_call_args , v0
+#define __flatbuffers_int16_formal_args , int16_t v0
+#define __flatbuffers_int16_call_args , v0
+#define __flatbuffers_int32_formal_args , int32_t v0
+#define __flatbuffers_int32_call_args , v0
+#define __flatbuffers_int64_formal_args , int64_t v0
+#define __flatbuffers_int64_call_args , v0
+#define __flatbuffers_float_formal_args , float v0
+#define __flatbuffers_float_call_args , v0
+#define __flatbuffers_double_formal_args , double v0
+#define __flatbuffers_double_call_args , v0
+
+__flatbuffers_build_scalar(flatbuffers_, flatbuffers_uint8, uint8_t)
+__flatbuffers_build_scalar(flatbuffers_, flatbuffers_int8, int8_t)
+__flatbuffers_build_scalar(flatbuffers_, flatbuffers_bool, flatbuffers_bool_t)
+__flatbuffers_build_scalar(flatbuffers_, flatbuffers_uint16, uint16_t)
+__flatbuffers_build_scalar(flatbuffers_, flatbuffers_uint32, uint32_t)
+__flatbuffers_build_scalar(flatbuffers_, flatbuffers_uint64, uint64_t)
+__flatbuffers_build_scalar(flatbuffers_, flatbuffers_int16, int16_t)
+__flatbuffers_build_scalar(flatbuffers_, flatbuffers_int32, int32_t)
+__flatbuffers_build_scalar(flatbuffers_, flatbuffers_int64, int64_t)
+__flatbuffers_build_scalar(flatbuffers_, flatbuffers_float, float)
+__flatbuffers_build_scalar(flatbuffers_, flatbuffers_double, double)
+
+__flatbuffers_build_string(flatbuffers_)
+
+__flatbuffers_build_buffer(flatbuffers_)
+#include "flatcc/portable/pdiagnostic_pop.h"
+#endif /* FLATBUFFERS_COMMON_BUILDER_H */
diff --git a/Source/library/FBSUtil/flatcc/reflection/flatbuffers_common_reader.h b/Source/library/FBSUtil/flatcc/reflection/flatbuffers_common_reader.h
new file mode 100644
index 0000000..fc9e3fa
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/reflection/flatbuffers_common_reader.h
@@ -0,0 +1,431 @@
+#ifndef FLATBUFFERS_COMMON_READER_H
+#define FLATBUFFERS_COMMON_READER_H
+
+/* Generated by flatcc 0.4.2 FlatBuffers schema compiler for C by dvide.com */
+
+/* Common FlatBuffers read functionality for C. */
+
+#define PDIAGNOSTIC_IGNORE_UNUSED
+#include "flatcc/portable/pdiagnostic_push.h"
+#include "flatcc/flatcc_flatbuffers.h"
+
+
+#define __flatbuffers_read_scalar_at_byteoffset(N, p, o) N ## _read_from_pe((uint8_t *)(p) + (o))
+#define __flatbuffers_read_scalar(N, p) N ## _read_from_pe(p)
+#define __flatbuffers_read_vt(ID, offset, t)\
+flatbuffers_voffset_t offset = 0;\
+{ flatbuffers_voffset_t id, *vt;\
+ assert(t != 0 && "null pointer table access");\
+ id = ID;\
+ vt = (flatbuffers_voffset_t *)((uint8_t *)(t) -\
+ __flatbuffers_soffset_read_from_pe(t));\
+ if (__flatbuffers_voffset_read_from_pe(vt) >= sizeof(vt[0]) * (id + 3)) {\
+ offset = __flatbuffers_voffset_read_from_pe(vt + id + 2);\
+ }\
+}
+#define __flatbuffers_field_present(ID, t) { __flatbuffers_read_vt(ID, offset, t) return offset != 0; }
+#define __flatbuffers_union_type_field(ID, t)\
+{\
+ __flatbuffers_read_vt(ID, offset, t)\
+ return offset ? __flatbuffers_read_scalar_at_byteoffset(__flatbuffers_utype, t, offset) : 0;\
+}
+#define __flatbuffers_define_union_field(ID, N, NK, r)\
+static inline flatbuffers_utype_t N ## _ ## NK ## _type(N ## _table_t t)\
+__flatbuffers_union_type_field(((ID) - 1), t)\
+static inline flatbuffers_generic_table_t N ## _ ## NK(N ## _table_t t)\
+__flatbuffers_table_field(flatbuffers_generic_table_t, ID, t, r)\
+static inline int N ## _ ## NK ## _is_present(N ## _table_t t)\
+__flatbuffers_field_present(ID, t)
+#define __flatbuffers_define_scalar_field(ID, N, NK, TK, T, V)\
+static inline T N ## _ ## NK (N ## _table_t t)\
+{ __flatbuffers_read_vt(ID, offset, t)\
+ return offset ? __flatbuffers_read_scalar_at_byteoffset(TK, t, offset) : V;\
+}\
+static inline int N ## _ ## NK ## _is_present(N ## _table_t t)\
+__flatbuffers_field_present(ID, t)\
+__flatbuffers_define_scan_by_scalar_field(N, NK, T)
+#define __flatbuffers_struct_field(T, ID, t, r)\
+{\
+ __flatbuffers_read_vt(ID, offset, t)\
+ if (offset) {\
+ return (T)((uint8_t *)(t) + offset);\
+ }\
+ assert(!(r) && "required field missing");\
+ return 0;\
+}
+#define __flatbuffers_offset_field(T, ID, t, r, adjust)\
+{\
+ flatbuffers_uoffset_t *elem;\
+ __flatbuffers_read_vt(ID, offset, t)\
+ if (offset) {\
+ elem = (flatbuffers_uoffset_t *)((uint8_t *)(t) + offset);\
+ /* Add sizeof so C api can have raw access past header field. */\
+ return (T)((uint8_t *)(elem) + adjust +\
+ __flatbuffers_uoffset_read_from_pe(elem));\
+ }\
+ assert(!(r) && "required field missing");\
+ return 0;\
+}
+#define __flatbuffers_vector_field(T, ID, t, r) __flatbuffers_offset_field(T, ID, t, r, sizeof(flatbuffers_uoffset_t))
+#define __flatbuffers_table_field(T, ID, t, r) __flatbuffers_offset_field(T, ID, t, r, 0)
+#define __flatbuffers_define_struct_field(ID, N, NK, T, r)\
+static inline T N ## _ ## NK(N ## _table_t t)\
+__flatbuffers_struct_field(T, ID, t, r)\
+static inline int N ## _ ## NK ## _is_present(N ## _table_t t)\
+__flatbuffers_field_present(ID, t)
+#define __flatbuffers_define_vector_field(ID, N, NK, T, r)\
+static inline T N ## _ ## NK(N ## _table_t t)\
+__flatbuffers_vector_field(T, ID, t, r)\
+static inline int N ## _ ## NK ## _is_present(N ## _table_t t)\
+__flatbuffers_field_present(ID, t)
+#define __flatbuffers_define_table_field(ID, N, NK, T, r)\
+static inline T N ## _ ## NK(N ## _table_t t)\
+__flatbuffers_table_field(T, ID, t, r)\
+static inline int N ## _ ## NK ## _is_present(N ## _table_t t)\
+__flatbuffers_field_present(ID, t)
+#define __flatbuffers_define_string_field(ID, N, NK, r)\
+static inline flatbuffers_string_t N ## _ ## NK(N ## _table_t t)\
+__flatbuffers_vector_field(flatbuffers_string_t, ID, t, r)\
+static inline int N ## _ ## NK ## _is_present(N ## _table_t t)\
+__flatbuffers_field_present(ID, t)\
+__flatbuffers_define_scan_by_string_field(N, NK)
+#define __flatbuffers_vec_len(vec)\
+{ return (vec) ? (size_t)__flatbuffers_uoffset_read_from_pe((flatbuffers_uoffset_t *)vec - 1) : 0; }
+#define __flatbuffers_string_len(s) __flatbuffers_vec_len(s)
+static inline size_t flatbuffers_vec_len(const void *vec)
+__flatbuffers_vec_len(vec)
+#define __flatbuffers_scalar_vec_at(N, vec, i)\
+{ assert(flatbuffers_vec_len(vec) > (i) && "index out of range");\
+ return __flatbuffers_read_scalar(N, &(vec)[i]); }
+#define __flatbuffers_struct_vec_at(vec, i)\
+{ assert(flatbuffers_vec_len(vec) > (i) && "index out of range"); return (vec) + (i); }
+/* `adjust` skips past the header for string vectors. */
+#define __flatbuffers_offset_vec_at(T, vec, i, adjust)\
+{ const flatbuffers_uoffset_t *elem = (vec) + (i);\
+ assert(flatbuffers_vec_len(vec) > (i) && "index out of range");\
+ return (T)((uint8_t *)(elem) + (size_t)__flatbuffers_uoffset_read_from_pe(elem) + adjust); }
+#define __flatbuffers_define_scalar_vec_len(N) \
+static inline size_t N ## _vec_len(N ##_vec_t vec)\
+{ return flatbuffers_vec_len(vec); }
+#define __flatbuffers_define_scalar_vec_at(N, T) \
+static inline T N ## _vec_at(N ## _vec_t vec, size_t i)\
+__flatbuffers_scalar_vec_at(N, vec, i)
+typedef const char *flatbuffers_string_t;
+static inline size_t flatbuffers_string_len(flatbuffers_string_t s)
+__flatbuffers_string_len(s)
+typedef const flatbuffers_uoffset_t *flatbuffers_string_vec_t;
+typedef flatbuffers_uoffset_t *flatbuffers_string_mutable_vec_t;
+static inline size_t flatbuffers_string_vec_len(flatbuffers_string_vec_t vec)
+__flatbuffers_vec_len(vec)
+static inline flatbuffers_string_t flatbuffers_string_vec_at(flatbuffers_string_vec_t vec, size_t i)
+__flatbuffers_offset_vec_at(flatbuffers_string_t, vec, i, sizeof(vec[0]))
+typedef const void *flatbuffers_generic_table_t;
+#include
+static size_t flatbuffers_not_found = (size_t)-1;
+static size_t flatbuffers_end = (size_t)-1;
+#define __flatbuffers_identity(n) (n)
+#define __flatbuffers_min(a, b) ((a) < (b) ? (a) : (b))
+/* Subtraction doesn't work for unsigned types. */
+#define __flatbuffers_scalar_cmp(x, y, n) ((x) < (y) ? -1 : (x) > (y))
+static inline int __flatbuffers_string_n_cmp(flatbuffers_string_t v, const char *s, size_t n)
+{ size_t nv = flatbuffers_string_len(v); int x = strncmp(v, s, nv < n ? nv : n);
+ return x != 0 ? x : nv < n ? -1 : nv > n; }
+/* `n` arg unused, but needed by string find macro expansion. */
+static inline int __flatbuffers_string_cmp(flatbuffers_string_t v, const char *s, size_t n) { (void)n; return strcmp(v, s); }
+/* A = identity if searching scalar vectors rather than key fields. */
+/* Returns lowest matching index or not_found. */
+#define __flatbuffers_find_by_field(A, V, E, L, K, Kn, T, D)\
+{ T v; size_t a = 0, b, m; if (!(b = L(V))) { return flatbuffers_not_found; }\
+ --b;\
+ while (a < b) {\
+ m = a + ((b - a) >> 1);\
+ v = A(E(V, m));\
+ if ((D(v, (K), (Kn))) < 0) {\
+ a = m + 1;\
+ } else {\
+ b = m;\
+ }\
+ }\
+ if (a == b) {\
+ v = A(E(V, a));\
+ if (D(v, (K), (Kn)) == 0) {\
+ return a;\
+ }\
+ }\
+ return flatbuffers_not_found;\
+}
+#define __flatbuffers_find_by_scalar_field(A, V, E, L, K, T)\
+__flatbuffers_find_by_field(A, V, E, L, K, 0, T, __flatbuffers_scalar_cmp)
+#define __flatbuffers_find_by_string_field(A, V, E, L, K)\
+__flatbuffers_find_by_field(A, V, E, L, K, 0, flatbuffers_string_t, __flatbuffers_string_cmp)
+#define __flatbuffers_find_by_string_n_field(A, V, E, L, K, Kn)\
+__flatbuffers_find_by_field(A, V, E, L, K, Kn, flatbuffers_string_t, __flatbuffers_string_n_cmp)
+#define __flatbuffers_define_find_by_scalar_field(N, NK, TK)\
+static inline size_t N ## _vec_find_by_ ## NK(N ## _vec_t vec, TK key)\
+__flatbuffers_find_by_scalar_field(N ## _ ## NK, vec, N ## _vec_at, N ## _vec_len, key, TK)
+#define __flatbuffers_define_scalar_find(N, T)\
+static inline size_t N ## _vec_find(N ## _vec_t vec, T key)\
+__flatbuffers_find_by_scalar_field(__flatbuffers_identity, vec, N ## _vec_at, N ## _vec_len, key, T)
+#define __flatbuffers_define_find_by_string_field(N, NK) \
+/* Note: find only works on vectors sorted by this field. */\
+static inline size_t N ## _vec_find_by_ ## NK(N ## _vec_t vec, const char *s)\
+__flatbuffers_find_by_string_field(N ## _ ## NK, vec, N ## _vec_at, N ## _vec_len, s)\
+static inline size_t N ## _vec_find_n_by_ ## NK(N ## _vec_t vec, const char *s, int n)\
+__flatbuffers_find_by_string_n_field(N ## _ ## NK, vec, N ## _vec_at, N ## _vec_len, s, n)
+#define __flatbuffers_define_default_find_by_scalar_field(N, NK, TK)\
+static inline size_t N ## _vec_find(N ## _vec_t vec, TK key)\
+{ return N ## _vec_find_by_ ## NK(vec, key); }
+#define __flatbuffers_define_default_find_by_string_field(N, NK) \
+static inline size_t N ## _vec_find(N ## _vec_t vec, const char *s)\
+{ return N ## _vec_find_by_ ## NK(vec, s); }\
+static inline size_t N ## _vec_find_n(N ## _vec_t vec, const char *s, int n)\
+{ return N ## _vec_find_n_by_ ## NK(vec, s, n); }
+/* A = identity if searching scalar vectors rather than key fields. */
+/* Returns lowest matching index or not_found. */
+#define __flatbuffers_scan_by_field(b, e, A, V, E, L, K, Kn, T, D)\
+{ T v; size_t i;\
+ for (i = b; i < e; ++i) {\
+ v = A(E(V, i));\
+ if (D(v, (K), (Kn)) == 0) {\
+ return i;\
+ }\
+ }\
+ return flatbuffers_not_found;\
+}
+#define __flatbuffers_rscan_by_field(b, e, A, V, E, L, K, Kn, T, D)\
+{ T v; size_t i = e;\
+ while (i-- > b) {\
+ v = A(E(V, i));\
+ if (D(v, (K), (Kn)) == 0) {\
+ return i;\
+ }\
+ }\
+ return flatbuffers_not_found;\
+}
+#define __flatbuffers_scan_by_scalar_field(b, e, A, V, E, L, K, T)\
+__flatbuffers_scan_by_field(b, e, A, V, E, L, K, 0, T, __flatbuffers_scalar_cmp)
+#define __flatbuffers_scan_by_string_field(b, e, A, V, E, L, K)\
+__flatbuffers_scan_by_field(b, e, A, V, E, L, K, 0, flatbuffers_string_t, __flatbuffers_string_cmp)
+#define __flatbuffers_scan_by_string_n_field(b, e, A, V, E, L, K, Kn)\
+__flatbuffers_scan_by_field(b, e, A, V, E, L, K, Kn, flatbuffers_string_t, __flatbuffers_string_n_cmp)
+#define __flatbuffers_rscan_by_scalar_field(b, e, A, V, E, L, K, T)\
+__flatbuffers_rscan_by_field(b, e, A, V, E, L, K, 0, T, __flatbuffers_scalar_cmp)
+#define __flatbuffers_rscan_by_string_field(b, e, A, V, E, L, K)\
+__flatbuffers_rscan_by_field(b, e, A, V, E, L, K, 0, flatbuffers_string_t, __flatbuffers_string_cmp)
+#define __flatbuffers_rscan_by_string_n_field(b, e, A, V, E, L, K, Kn)\
+__flatbuffers_rscan_by_field(b, e, A, V, E, L, K, Kn, flatbuffers_string_t, __flatbuffers_string_n_cmp)
+#define __flatbuffers_define_scan_by_scalar_field(N, NK, T)\
+static inline size_t N ## _vec_scan_by_ ## NK(N ## _vec_t vec, T key)\
+__flatbuffers_scan_by_scalar_field(0, N ## _vec_len(vec), N ## _ ## NK, vec, N ## _vec_at, N ## _vec_len, key, T)\
+static inline size_t N ## _vec_scan_ex_by_ ## NK(N ## _vec_t vec, size_t begin, size_t end, T key)\
+__flatbuffers_scan_by_scalar_field(begin, __flatbuffers_min(end, N ## _vec_len(vec)), N ## _ ## NK, vec, N ## _vec_at, N ## _vec_len, key, T)\
+static inline size_t N ## _vec_rscan_by_ ## NK(N ## _vec_t vec, T key)\
+__flatbuffers_rscan_by_scalar_field(0, N ## _vec_len(vec), N ## _ ## NK, vec, N ## _vec_at, N ## _vec_len, key, T)\
+static inline size_t N ## _vec_rscan_ex_by_ ## NK(N ## _vec_t vec, size_t begin, size_t end, T key)\
+__flatbuffers_rscan_by_scalar_field(begin, __flatbuffers_min(end, N ## _vec_len(vec)), N ## _ ## NK, vec, N ## _vec_at, N ## _vec_len, key, T)
+#define __flatbuffers_define_scalar_scan(N, T)\
+static inline size_t N ## _vec_scan(N ## _vec_t vec, T key)\
+__flatbuffers_scan_by_scalar_field(0, N ## _vec_len(vec), __flatbuffers_identity, vec, N ## _vec_at, N ## _vec_len, key, T)\
+static inline size_t N ## _vec_scan_ex(N ## _vec_t vec, size_t begin, size_t end, T key)\
+__flatbuffers_scan_by_scalar_field(begin, __flatbuffers_min(end, N ## _vec_len(vec)), __flatbuffers_identity, vec, N ## _vec_at, N ## _vec_len, key, T)\
+static inline size_t N ## _vec_rscan(N ## _vec_t vec, T key)\
+__flatbuffers_rscan_by_scalar_field(0, N ## _vec_len(vec), __flatbuffers_identity, vec, N ## _vec_at, N ## _vec_len, key, T)\
+static inline size_t N ## _vec_rscan_ex(N ## _vec_t vec, size_t begin, size_t end, T key)\
+__flatbuffers_rscan_by_scalar_field(begin, __flatbuffers_min(end, N ## _vec_len(vec)), __flatbuffers_identity, vec, N ## _vec_at, N ## _vec_len, key, T)
+#define __flatbuffers_define_scan_by_string_field(N, NK) \
+static inline size_t N ## _vec_scan_by_ ## NK(N ## _vec_t vec, const char *s)\
+__flatbuffers_scan_by_string_field(0, N ## _vec_len(vec), N ## _ ## NK, vec, N ## _vec_at, N ## _vec_len, s)\
+static inline size_t N ## _vec_scan_n_by_ ## NK(N ## _vec_t vec, const char *s, int n)\
+__flatbuffers_scan_by_string_n_field(0, N ## _vec_len(vec), N ## _ ## NK, vec, N ## _vec_at, N ## _vec_len, s, n)\
+static inline size_t N ## _vec_scan_ex_by_ ## NK(N ## _vec_t vec, size_t begin, size_t end, const char *s)\
+__flatbuffers_scan_by_string_field(begin, __flatbuffers_min(end, N ## _vec_len(vec)), N ## _ ## NK, vec, N ## _vec_at, N ## _vec_len, s)\
+static inline size_t N ## _vec_scan_ex_n_by_ ## NK(N ## _vec_t vec, size_t begin, size_t end, const char *s, int n)\
+__flatbuffers_scan_by_string_n_field(begin, __flatbuffers_min( end, N ## _vec_len(vec) ), N ## _ ## NK, vec, N ## _vec_at, N ## _vec_len, s, n)\
+static inline size_t N ## _vec_rscan_by_ ## NK(N ## _vec_t vec, const char *s)\
+__flatbuffers_rscan_by_string_field(0, N ## _vec_len(vec), N ## _ ## NK, vec, N ## _vec_at, N ## _vec_len, s)\
+static inline size_t N ## _vec_rscan_n_by_ ## NK(N ## _vec_t vec, const char *s, int n)\
+__flatbuffers_rscan_by_string_n_field(0, N ## _vec_len(vec), N ## _ ## NK, vec, N ## _vec_at, N ## _vec_len, s, n)\
+static inline size_t N ## _vec_rscan_ex_by_ ## NK(N ## _vec_t vec, size_t begin, size_t end, const char *s)\
+__flatbuffers_rscan_by_string_field(begin, __flatbuffers_min(end, N ## _vec_len(vec)), N ## _ ## NK, vec, N ## _vec_at, N ## _vec_len, s)\
+static inline size_t N ## _vec_rscan_ex_n_by_ ## NK(N ## _vec_t vec, size_t begin, size_t end, const char *s, int n)\
+__flatbuffers_rscan_by_string_n_field(begin, __flatbuffers_min( end, N ## _vec_len(vec) ), N ## _ ## NK, vec, N ## _vec_at, N ## _vec_len, s, n)
+#define __flatbuffers_define_default_scan_by_scalar_field(N, NK, TK)\
+static inline size_t N ## _vec_scan(N ## _vec_t vec, TK key)\
+{ return N ## _vec_scan_by_ ## NK(vec, key); }\
+static inline size_t N ## _vec_scan_ex(N ## _vec_t vec, size_t begin, size_t end, TK key)\
+{ return N ## _vec_scan_ex_by_ ## NK(vec, begin, end, key); }\
+static inline size_t N ## _vec_rscan(N ## _vec_t vec, TK key)\
+{ return N ## _vec_rscan_by_ ## NK(vec, key); }\
+static inline size_t N ## _vec_rscan_ex(N ## _vec_t vec, size_t begin, size_t end, TK key)\
+{ return N ## _vec_rscan_ex_by_ ## NK(vec, begin, end, key); }
+#define __flatbuffers_define_default_scan_by_string_field(N, NK) \
+static inline size_t N ## _vec_scan(N ## _vec_t vec, const char *s)\
+{ return N ## _vec_scan_by_ ## NK(vec, s); }\
+static inline size_t N ## _vec_scan_n(N ## _vec_t vec, const char *s, int n)\
+{ return N ## _vec_scan_n_by_ ## NK(vec, s, n); }\
+static inline size_t N ## _vec_scan_ex(N ## _vec_t vec, size_t begin, size_t end, const char *s)\
+{ return N ## _vec_scan_ex_by_ ## NK(vec, begin, end, s); }\
+static inline size_t N ## _vec_scan_ex_n(N ## _vec_t vec, size_t begin, size_t end, const char *s, int n)\
+{ return N ## _vec_scan_ex_n_by_ ## NK(vec, begin, end, s, n); }\
+static inline size_t N ## _vec_rscan(N ## _vec_t vec, const char *s)\
+{ return N ## _vec_rscan_by_ ## NK(vec, s); }\
+static inline size_t N ## _vec_rscan_n(N ## _vec_t vec, const char *s, int n)\
+{ return N ## _vec_rscan_n_by_ ## NK(vec, s, n); }\
+static inline size_t N ## _vec_rscan_ex(N ## _vec_t vec, size_t begin, size_t end, const char *s)\
+{ return N ## _vec_rscan_ex_by_ ## NK(vec, begin, end, s); }\
+static inline size_t N ## _vec_rscan_ex_n(N ## _vec_t vec, size_t begin, size_t end, const char *s, int n)\
+{ return N ## _vec_rscan_ex_n_by_ ## NK(vec, begin, end, s, n); }
+#define __flatbuffers_heap_sort(N, X, A, E, L, TK, TE, D, S)\
+static inline void __ ## N ## X ## __heap_sift_down(\
+ N ## _mutable_vec_t vec, size_t start, size_t end)\
+{ size_t child, root; TK v1, v2, vroot;\
+ root = start;\
+ while ((root << 1) <= end) {\
+ child = root << 1;\
+ if (child < end) {\
+ v1 = A(E(vec, child));\
+ v2 = A(E(vec, child + 1));\
+ if (D(v1, v2) < 0) {\
+ child++;\
+ }\
+ }\
+ vroot = A(E(vec, root));\
+ v1 = A(E(vec, child));\
+ if (D(vroot, v1) < 0) {\
+ S(vec, root, child, TE);\
+ root = child;\
+ } else {\
+ return;\
+ }\
+ }\
+}\
+static inline void __ ## N ## X ## __heap_sort(N ## _mutable_vec_t vec)\
+{ size_t start, end, size;\
+ size = L(vec); if (size == 0) return; end = size - 1; start = size >> 1;\
+ do { __ ## N ## X ## __heap_sift_down(vec, start, end); } while (start--);\
+ while (end > 0) { \
+ S(vec, 0, end, TE);\
+ __ ## N ## X ## __heap_sift_down(vec, 0, --end); } }
+#define __flatbuffers_define_sort_by_field(N, NK, TK, TE, D, S)\
+ __flatbuffers_heap_sort(N, _sort_by_ ## NK, N ## _ ## NK, N ## _vec_at, N ## _vec_len, TK, TE, D, S)\
+static inline void N ## _vec_sort_by_ ## NK(N ## _mutable_vec_t vec)\
+{ __ ## N ## _sort_by_ ## NK ## __heap_sort(vec); }
+#define __flatbuffers_define_sort(N, TK, TE, D, S)\
+__flatbuffers_heap_sort(N, , __flatbuffers_identity, N ## _vec_at, N ## _vec_len, TK, TE, D, S)\
+static inline void N ## _vec_sort(N ## _mutable_vec_t vec) { __ ## N ## __heap_sort(vec); }
+#define __flatbuffers_scalar_diff(x, y) ((x) < (y) ? -1 : (x) > (y))
+#define __flatbuffers_string_diff(x, y) __flatbuffers_string_n_cmp((x), (const char *)(y), flatbuffers_string_len(y))
+#define __flatbuffers_scalar_swap(vec, a, b, TE) { TE tmp = vec[b]; vec[b] = vec[a]; vec[a] = tmp; }
+#define __flatbuffers_string_swap(vec, a, b, TE)\
+{ TE ta, tb, d;\
+ d = (TE)((a - b) * sizeof(vec[0]));\
+ ta = __flatbuffers_uoffset_read_from_pe(vec + b) - d;\
+ tb = __flatbuffers_uoffset_read_from_pe(vec + a) + d;\
+ __flatbuffers_uoffset_write_to_pe(vec + a, ta);\
+ __flatbuffers_uoffset_write_to_pe(vec + b, tb); }
+#define __flatbuffers_define_sort_by_scalar_field(N, NK, TK, TE)\
+ __flatbuffers_define_sort_by_field(N, NK, TK, TE, __flatbuffers_scalar_diff, __flatbuffers_scalar_swap)
+#define __flatbuffers_define_sort_by_string_field(N, NK)\
+ __flatbuffers_define_sort_by_field(N, NK, flatbuffers_string_t, flatbuffers_uoffset_t, __flatbuffers_string_diff, __flatbuffers_string_swap)
+#define __flatbuffers_define_scalar_sort(N, T) __flatbuffers_define_sort(N, T, T, __flatbuffers_scalar_diff, __flatbuffers_scalar_swap)
+#define __flatbuffers_define_string_sort() __flatbuffers_define_sort(flatbuffers_string, flatbuffers_string_t, flatbuffers_uoffset_t, __flatbuffers_string_diff, __flatbuffers_string_swap)
+#define __flatbuffers_define_scalar_vector(N, T)\
+typedef const T *N ## _vec_t;\
+typedef T *N ## _mutable_vec_t;\
+__flatbuffers_define_scalar_vec_len(N)\
+__flatbuffers_define_scalar_vec_at(N, T)\
+__flatbuffers_define_scalar_find(N, T)\
+__flatbuffers_define_scalar_scan(N, T)\
+__flatbuffers_define_scalar_sort(N, T)
+
+#define __flatbuffers_define_integer_type(N, T, W)\
+__flatcc_define_integer_accessors(N, T, W, flatbuffers_endian)\
+__flatbuffers_define_scalar_vector(N, T)
+__flatbuffers_define_scalar_vector(flatbuffers_bool, flatbuffers_bool_t)
+__flatbuffers_define_scalar_vector(flatbuffers_uint8, uint8_t)
+__flatbuffers_define_scalar_vector(flatbuffers_int8, int8_t)
+__flatbuffers_define_scalar_vector(flatbuffers_uint16, uint16_t)
+__flatbuffers_define_scalar_vector(flatbuffers_int16, int16_t)
+__flatbuffers_define_scalar_vector(flatbuffers_uint32, uint32_t)
+__flatbuffers_define_scalar_vector(flatbuffers_int32, int32_t)
+__flatbuffers_define_scalar_vector(flatbuffers_uint64, uint64_t)
+__flatbuffers_define_scalar_vector(flatbuffers_int64, int64_t)
+__flatbuffers_define_scalar_vector(flatbuffers_float, float)
+__flatbuffers_define_scalar_vector(flatbuffers_double, double)
+static inline size_t flatbuffers_string_vec_find(flatbuffers_string_vec_t vec, const char *s)
+__flatbuffers_find_by_string_field(__flatbuffers_identity, vec, flatbuffers_string_vec_at, flatbuffers_string_vec_len, s)
+static inline size_t flatbuffers_string_vec_find_n(flatbuffers_string_vec_t vec, const char *s, size_t n)
+__flatbuffers_find_by_string_n_field(__flatbuffers_identity, vec, flatbuffers_string_vec_at, flatbuffers_string_vec_len, s, n)
+static inline size_t flatbuffers_string_vec_scan(flatbuffers_string_vec_t vec, const char *s)
+__flatbuffers_scan_by_string_field(0, flatbuffers_string_vec_len(vec), __flatbuffers_identity, vec, flatbuffers_string_vec_at, flatbuffers_string_vec_len, s)
+static inline size_t flatbuffers_string_vec_scan_n(flatbuffers_string_vec_t vec, const char *s, size_t n)
+__flatbuffers_scan_by_string_n_field(0, flatbuffers_string_vec_len(vec), __flatbuffers_identity, vec, flatbuffers_string_vec_at, flatbuffers_string_vec_len, s, n)
+static inline size_t flatbuffers_string_vec_scan_ex(flatbuffers_string_vec_t vec, size_t begin, size_t end, const char *s)
+__flatbuffers_scan_by_string_field(begin, __flatbuffers_min(end, flatbuffers_string_vec_len(vec)), __flatbuffers_identity, vec, flatbuffers_string_vec_at, flatbuffers_string_vec_len, s)
+static inline size_t flatbuffers_string_vec_scan_ex_n(flatbuffers_string_vec_t vec, size_t begin, size_t end, const char *s, size_t n)
+__flatbuffers_scan_by_string_n_field(begin, __flatbuffers_min(end, flatbuffers_string_vec_len(vec)), __flatbuffers_identity, vec, flatbuffers_string_vec_at, flatbuffers_string_vec_len, s, n)
+static inline size_t flatbuffers_string_vec_rscan(flatbuffers_string_vec_t vec, const char *s)
+__flatbuffers_rscan_by_string_field(0, flatbuffers_string_vec_len(vec), __flatbuffers_identity, vec, flatbuffers_string_vec_at, flatbuffers_string_vec_len, s)
+static inline size_t flatbuffers_string_vec_rscan_n(flatbuffers_string_vec_t vec, const char *s, size_t n)
+__flatbuffers_rscan_by_string_n_field(0, flatbuffers_string_vec_len(vec), __flatbuffers_identity, vec, flatbuffers_string_vec_at, flatbuffers_string_vec_len, s, n)
+static inline size_t flatbuffers_string_vec_rscan_ex(flatbuffers_string_vec_t vec, size_t begin, size_t end, const char *s)
+__flatbuffers_rscan_by_string_field(begin, __flatbuffers_min(end, flatbuffers_string_vec_len(vec)), __flatbuffers_identity, vec, flatbuffers_string_vec_at, flatbuffers_string_vec_len, s)
+static inline size_t flatbuffers_string_vec_rscan_ex_n(flatbuffers_string_vec_t vec, size_t begin, size_t end, const char *s, size_t n)
+__flatbuffers_rscan_by_string_n_field(begin, __flatbuffers_min(end, flatbuffers_string_vec_len(vec)), __flatbuffers_identity, vec, flatbuffers_string_vec_at, flatbuffers_string_vec_len, s, n)
+__flatbuffers_define_string_sort()
+#define __flatbuffers_define_struct_scalar_field(N, NK, TK, T)\
+static inline T N ## _ ## NK (N ## _struct_t t)\
+{ return t ? __flatbuffers_read_scalar(TK, &(t->NK)) : 0; }\
+__flatbuffers_define_scan_by_scalar_field(N, NK, T)
+#define __flatbuffers_define_struct_struct_field(N, NK, T)\
+static inline T N ## _ ## NK(N ## _struct_t t) { return t ? &(t->NK) : 0; }
+/* If fid is null, the function returns true without testing as buffer is not expected to have any id. */
+static inline int flatbuffers_has_identifier(const void *buffer, const char *fid)
+{ flatbuffers_thash_t id, id2 = 0; if (fid == 0) { return 1; };
+ strncpy((char *)&id2, fid, sizeof(id2));
+ /* Identifier strings are always considered little endian. */
+ id2 = __flatbuffers_thash_cast_from_le(id2);
+ id = __flatbuffers_thash_read_from_pe(((flatbuffers_uoffset_t *)buffer) + 1);
+ return id2 == 0 || id == id2; }
+static inline int flatbuffers_has_type_hash(const void *buffer, flatbuffers_thash_t thash)
+{ return thash == 0 || (__flatbuffers_thash_read_from_pe((flatbuffers_uoffset_t *)buffer + 1) == thash); }
+
+static inline flatbuffers_thash_t flatbuffers_get_type_hash(const void *buffer)
+{ return __flatbuffers_thash_read_from_pe((flatbuffers_uoffset_t *)buffer + 1); }
+
+#define flatbuffers_verify_endian() flatbuffers_has_identifier("\x00\x00\x00\x00" "1234", "1234")
+static inline void *flatbuffers_read_size_prefix(void *b, size_t *size_out)
+{ if (size_out) { *size_out = (size_t)__flatbuffers_uoffset_read_from_pe(b); }
+ return (uint8_t *)b + sizeof(flatbuffers_uoffset_t); }
+/* Null file identifier accepts anything, otherwise fid should be 4 characters. */
+#define __flatbuffers_read_root(T, K, buffer, fid)\
+ ((!buffer || !flatbuffers_has_identifier(buffer, fid)) ? 0 :\
+ ((T ## _ ## K ## t)(((uint8_t *)buffer) +\
+ __flatbuffers_uoffset_read_from_pe(buffer))))
+#define __flatbuffers_read_typed_root(T, K, buffer, thash)\
+ ((!buffer || !flatbuffers_has_type_hash(buffer, thash)) ? 0 :\
+ ((T ## _ ## K ## t)(((uint8_t *)buffer) +\
+ __flatbuffers_uoffset_read_from_pe(buffer))))
+#define __flatbuffers_nested_buffer_as_root(C, N, T, K)\
+static inline T ## _ ## K ## t C ## _ ## N ## _as_root_with_identifier(C ## _ ## table_t t, const char *fid)\
+{ const uint8_t *buffer = C ## _ ## N(t); return __flatbuffers_read_root(T, K, buffer, fid); }\
+static inline T ## _ ## K ## t C ## _ ## N ## _as_typed_root(C ## _ ## table_t t)\
+{ const uint8_t *buffer = C ## _ ## N(t); return __flatbuffers_read_root(T, K, buffer, C ## _ ## type_identifier); }\
+static inline T ## _ ## K ## t C ## _ ## N ## _as_root(C ## _ ## table_t t)\
+{ const char *fid = T ## _identifier;\
+ const uint8_t *buffer = C ## _ ## N(t); return __flatbuffers_read_root(T, K, buffer, fid); }
+#define __flatbuffers_buffer_as_root(N, K)\
+static inline N ## _ ## K ## t N ## _as_root_with_identifier(const void *buffer, const char *fid)\
+{ return __flatbuffers_read_root(N, K, buffer, fid); }\
+static inline N ## _ ## K ## t N ## _as_root_with_type_hash(const void *buffer, flatbuffers_thash_t thash)\
+{ return __flatbuffers_read_typed_root(N, K, buffer, thash); }\
+static inline N ## _ ## K ## t N ## _as_root(const void *buffer)\
+{ const char *fid = N ## _identifier;\
+ return __flatbuffers_read_root(N, K, buffer, fid); }\
+static inline N ## _ ## K ## t N ## _as_typed_root(const void *buffer)\
+{ return __flatbuffers_read_typed_root(N, K, buffer, N ## _type_hash); }
+#define __flatbuffers_struct_as_root(N) __flatbuffers_buffer_as_root(N, struct_)
+#define __flatbuffers_table_as_root(N) __flatbuffers_buffer_as_root(N, table_)
+
+#include "flatcc/portable/pdiagnostic_pop.h"
+#endif /* FLATBUFFERS_COMMON_H */
diff --git a/Source/library/FBSUtil/flatcc/reflection/reflection_builder.h b/Source/library/FBSUtil/flatcc/reflection/reflection_builder.h
new file mode 100644
index 0000000..52d7cfe
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/reflection/reflection_builder.h
@@ -0,0 +1,185 @@
+#ifndef REFLECTION_BUILDER_H
+#define REFLECTION_BUILDER_H
+
+/* Generated by flatcc 0.4.2 FlatBuffers schema compiler for C by dvide.com */
+
+#ifndef REFLECTION_READER_H
+#include "reflection_reader.h"
+#endif
+#ifndef FLATBUFFERS_COMMON_BUILDER_H
+#include "flatbuffers_common_builder.h"
+#endif
+#define PDIAGNOSTIC_IGNORE_UNUSED
+#include "flatcc/portable/pdiagnostic_push.h"
+#undef flatbuffers_identifier
+#define flatbuffers_identifier "BFBS"
+#undef flatbuffers_extension
+#define flatbuffers_extension ".bfbs"
+
+#define __reflection_BaseType_formal_args , reflection_BaseType_enum_t v0
+#define __reflection_BaseType_call_args , v0
+__flatbuffers_build_scalar(flatbuffers_, reflection_BaseType, reflection_BaseType_enum_t)
+
+static const flatbuffers_voffset_t __reflection_Type_required[] = { 0 };
+__flatbuffers_build_table(flatbuffers_, reflection_Type, 3)
+static const flatbuffers_voffset_t __reflection_EnumVal_required[] = { 0, 0 };
+__flatbuffers_build_table(flatbuffers_, reflection_EnumVal, 3)
+static const flatbuffers_voffset_t __reflection_Enum_required[] = { 0, 1, 3, 0 };
+__flatbuffers_build_table(flatbuffers_, reflection_Enum, 4)
+static const flatbuffers_voffset_t __reflection_Field_required[] = { 0, 1, 0 };
+__flatbuffers_build_table(flatbuffers_, reflection_Field, 9)
+static const flatbuffers_voffset_t __reflection_Object_required[] = { 0, 1, 0 };
+__flatbuffers_build_table(flatbuffers_, reflection_Object, 5)
+static const flatbuffers_voffset_t __reflection_Schema_required[] = { 0, 1, 0 };
+__flatbuffers_build_table(flatbuffers_, reflection_Schema, 5)
+#define __reflection_Type_formal_args , reflection_BaseType_enum_t v0, reflection_BaseType_enum_t v1, int32_t v2
+#define __reflection_Type_call_args , v0, v1, v2
+static inline reflection_Type_ref_t reflection_Type_create(flatbuffers_builder_t *B __reflection_Type_formal_args);
+#define __reflection_EnumVal_formal_args , flatbuffers_string_ref_t v0, int64_t v1, reflection_Object_ref_t v2
+#define __reflection_EnumVal_call_args , v0, v1, v2
+static inline reflection_EnumVal_ref_t reflection_EnumVal_create(flatbuffers_builder_t *B __reflection_EnumVal_formal_args);
+#define __reflection_Enum_formal_args , flatbuffers_string_ref_t v0, reflection_EnumVal_vec_ref_t v1, flatbuffers_bool_t v2, reflection_Type_ref_t v3
+#define __reflection_Enum_call_args , v0, v1, v2, v3
+static inline reflection_Enum_ref_t reflection_Enum_create(flatbuffers_builder_t *B __reflection_Enum_formal_args);
+#define __reflection_Field_formal_args ,\
+ flatbuffers_string_ref_t v0, reflection_Type_ref_t v1, uint16_t v2, uint16_t v3,\
+ int64_t v4, double v5, flatbuffers_bool_t v6, flatbuffers_bool_t v7, flatbuffers_bool_t v8
+#define __reflection_Field_call_args ,\
+ v0, v1, v2, v3,\
+ v4, v5, v6, v7, v8
+static inline reflection_Field_ref_t reflection_Field_create(flatbuffers_builder_t *B __reflection_Field_formal_args);
+#define __reflection_Object_formal_args ,\
+ flatbuffers_string_ref_t v0, reflection_Field_vec_ref_t v1, flatbuffers_bool_t v2, int32_t v3, int32_t v4
+#define __reflection_Object_call_args ,\
+ v0, v1, v2, v3, v4
+static inline reflection_Object_ref_t reflection_Object_create(flatbuffers_builder_t *B __reflection_Object_formal_args);
+#define __reflection_Schema_formal_args ,\
+ reflection_Object_vec_ref_t v0, reflection_Enum_vec_ref_t v1, flatbuffers_string_ref_t v2, flatbuffers_string_ref_t v3, reflection_Object_ref_t v4
+#define __reflection_Schema_call_args ,\
+ v0, v1, v2, v3, v4
+static inline reflection_Schema_ref_t reflection_Schema_create(flatbuffers_builder_t *B __reflection_Schema_formal_args);
+
+__flatbuffers_build_scalar_field(0, flatbuffers_, reflection_Type_base_type, reflection_BaseType, reflection_BaseType_enum_t, 1, 1, INT8_C(0))
+__flatbuffers_build_scalar_field(1, flatbuffers_, reflection_Type_element, reflection_BaseType, reflection_BaseType_enum_t, 1, 1, INT8_C(0))
+__flatbuffers_build_scalar_field(2, flatbuffers_, reflection_Type_index, flatbuffers_int32, int32_t, 4, 4, INT32_C(-1))
+
+static inline reflection_Type_ref_t reflection_Type_create(flatbuffers_builder_t *B __reflection_Type_formal_args)
+{
+ if (reflection_Type_start(B)
+ || reflection_Type_index_add(B, v2)
+ || reflection_Type_base_type_add(B, v0)
+ || reflection_Type_element_add(B, v1)) {
+ return 0;
+ }
+ return reflection_Type_end(B);
+}
+__flatbuffers_build_table_prolog(flatbuffers_, reflection_Type, reflection_Type_identifier, reflection_Type_type_identifier)
+
+__flatbuffers_build_string_field(0, flatbuffers_, reflection_EnumVal_name)
+__flatbuffers_build_scalar_field(1, flatbuffers_, reflection_EnumVal_value, flatbuffers_int64, int64_t, 8, 8, INT64_C(0))
+__flatbuffers_build_table_field(2, flatbuffers_, reflection_EnumVal_object, reflection_Object)
+
+static inline reflection_EnumVal_ref_t reflection_EnumVal_create(flatbuffers_builder_t *B __reflection_EnumVal_formal_args)
+{
+ if (reflection_EnumVal_start(B)
+ || reflection_EnumVal_value_add(B, v1)
+ || reflection_EnumVal_name_add(B, v0)
+ || reflection_EnumVal_object_add(B, v2)) {
+ return 0;
+ }
+ return reflection_EnumVal_end(B);
+}
+__flatbuffers_build_table_prolog(flatbuffers_, reflection_EnumVal, reflection_EnumVal_identifier, reflection_EnumVal_type_identifier)
+
+__flatbuffers_build_string_field(0, flatbuffers_, reflection_Enum_name)
+/* vector has keyed elements */
+__flatbuffers_build_table_vector_field(1, flatbuffers_, reflection_Enum_values, reflection_EnumVal)
+__flatbuffers_build_scalar_field(2, flatbuffers_, reflection_Enum_is_union, flatbuffers_bool, flatbuffers_bool_t, 1, 1, UINT8_C(0))
+__flatbuffers_build_table_field(3, flatbuffers_, reflection_Enum_underlying_type, reflection_Type)
+
+static inline reflection_Enum_ref_t reflection_Enum_create(flatbuffers_builder_t *B __reflection_Enum_formal_args)
+{
+ if (reflection_Enum_start(B)
+ || reflection_Enum_name_add(B, v0)
+ || reflection_Enum_values_add(B, v1)
+ || reflection_Enum_underlying_type_add(B, v3)
+ || reflection_Enum_is_union_add(B, v2)) {
+ return 0;
+ }
+ return reflection_Enum_end(B);
+}
+__flatbuffers_build_table_prolog(flatbuffers_, reflection_Enum, reflection_Enum_identifier, reflection_Enum_type_identifier)
+
+__flatbuffers_build_string_field(0, flatbuffers_, reflection_Field_name)
+__flatbuffers_build_table_field(1, flatbuffers_, reflection_Field_type, reflection_Type)
+__flatbuffers_build_scalar_field(2, flatbuffers_, reflection_Field_id, flatbuffers_uint16, uint16_t, 2, 2, UINT16_C(0))
+__flatbuffers_build_scalar_field(3, flatbuffers_, reflection_Field_offset, flatbuffers_uint16, uint16_t, 2, 2, UINT16_C(0))
+__flatbuffers_build_scalar_field(4, flatbuffers_, reflection_Field_default_integer, flatbuffers_int64, int64_t, 8, 8, INT64_C(0))
+__flatbuffers_build_scalar_field(5, flatbuffers_, reflection_Field_default_real, flatbuffers_double, double, 8, 8, 0.000000)
+__flatbuffers_build_scalar_field(6, flatbuffers_, reflection_Field_deprecated, flatbuffers_bool, flatbuffers_bool_t, 1, 1, UINT8_C(0))
+__flatbuffers_build_scalar_field(7, flatbuffers_, reflection_Field_required, flatbuffers_bool, flatbuffers_bool_t, 1, 1, UINT8_C(0))
+__flatbuffers_build_scalar_field(8, flatbuffers_, reflection_Field_key, flatbuffers_bool, flatbuffers_bool_t, 1, 1, UINT8_C(0))
+
+static inline reflection_Field_ref_t reflection_Field_create(flatbuffers_builder_t *B __reflection_Field_formal_args)
+{
+ if (reflection_Field_start(B)
+ || reflection_Field_default_integer_add(B, v4)
+ || reflection_Field_default_real_add(B, v5)
+ || reflection_Field_name_add(B, v0)
+ || reflection_Field_type_add(B, v1)
+ || reflection_Field_id_add(B, v2)
+ || reflection_Field_offset_add(B, v3)
+ || reflection_Field_deprecated_add(B, v6)
+ || reflection_Field_required_add(B, v7)
+ || reflection_Field_key_add(B, v8)) {
+ return 0;
+ }
+ return reflection_Field_end(B);
+}
+__flatbuffers_build_table_prolog(flatbuffers_, reflection_Field, reflection_Field_identifier, reflection_Field_type_identifier)
+
+__flatbuffers_build_string_field(0, flatbuffers_, reflection_Object_name)
+/* vector has keyed elements */
+__flatbuffers_build_table_vector_field(1, flatbuffers_, reflection_Object_fields, reflection_Field)
+__flatbuffers_build_scalar_field(2, flatbuffers_, reflection_Object_is_struct, flatbuffers_bool, flatbuffers_bool_t, 1, 1, UINT8_C(0))
+__flatbuffers_build_scalar_field(3, flatbuffers_, reflection_Object_minalign, flatbuffers_int32, int32_t, 4, 4, INT32_C(0))
+__flatbuffers_build_scalar_field(4, flatbuffers_, reflection_Object_bytesize, flatbuffers_int32, int32_t, 4, 4, INT32_C(0))
+
+static inline reflection_Object_ref_t reflection_Object_create(flatbuffers_builder_t *B __reflection_Object_formal_args)
+{
+ if (reflection_Object_start(B)
+ || reflection_Object_name_add(B, v0)
+ || reflection_Object_fields_add(B, v1)
+ || reflection_Object_minalign_add(B, v3)
+ || reflection_Object_bytesize_add(B, v4)
+ || reflection_Object_is_struct_add(B, v2)) {
+ return 0;
+ }
+ return reflection_Object_end(B);
+}
+__flatbuffers_build_table_prolog(flatbuffers_, reflection_Object, reflection_Object_identifier, reflection_Object_type_identifier)
+
+/* vector has keyed elements */
+__flatbuffers_build_table_vector_field(0, flatbuffers_, reflection_Schema_objects, reflection_Object)
+/* vector has keyed elements */
+__flatbuffers_build_table_vector_field(1, flatbuffers_, reflection_Schema_enums, reflection_Enum)
+__flatbuffers_build_string_field(2, flatbuffers_, reflection_Schema_file_ident)
+__flatbuffers_build_string_field(3, flatbuffers_, reflection_Schema_file_ext)
+__flatbuffers_build_table_field(4, flatbuffers_, reflection_Schema_root_table, reflection_Object)
+
+static inline reflection_Schema_ref_t reflection_Schema_create(flatbuffers_builder_t *B __reflection_Schema_formal_args)
+{
+ if (reflection_Schema_start(B)
+ || reflection_Schema_objects_add(B, v0)
+ || reflection_Schema_enums_add(B, v1)
+ || reflection_Schema_file_ident_add(B, v2)
+ || reflection_Schema_file_ext_add(B, v3)
+ || reflection_Schema_root_table_add(B, v4)) {
+ return 0;
+ }
+ return reflection_Schema_end(B);
+}
+__flatbuffers_build_table_prolog(flatbuffers_, reflection_Schema, reflection_Schema_identifier, reflection_Schema_type_identifier)
+
+#include "flatcc/portable/pdiagnostic_pop.h"
+#endif /* REFLECTION_BUILDER_H */
diff --git a/Source/library/FBSUtil/flatcc/reflection/reflection_reader.h b/Source/library/FBSUtil/flatcc/reflection/reflection_reader.h
new file mode 100644
index 0000000..a38e5e9
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/reflection/reflection_reader.h
@@ -0,0 +1,221 @@
+#ifndef REFLECTION_READER_H
+#define REFLECTION_READER_H
+
+/* Generated by flatcc 0.4.2 FlatBuffers schema compiler for C by dvide.com */
+
+#ifndef FLATBUFFERS_COMMON_READER_H
+#include "flatbuffers_common_reader.h"
+#endif
+#include "flatcc/flatcc_flatbuffers.h"
+#ifndef __alignas_is_defined
+#include
+#endif
+#define PDIAGNOSTIC_IGNORE_UNUSED
+#include "flatcc/portable/pdiagnostic_push.h"
+#undef flatbuffers_identifier
+#define flatbuffers_identifier "BFBS"
+#undef flatbuffers_extension
+#define flatbuffers_extension ".bfbs"
+
+
+typedef const struct reflection_Type_table *reflection_Type_table_t;
+typedef const flatbuffers_uoffset_t *reflection_Type_vec_t;
+typedef flatbuffers_uoffset_t *reflection_Type_mutable_vec_t;
+typedef const struct reflection_EnumVal_table *reflection_EnumVal_table_t;
+typedef const flatbuffers_uoffset_t *reflection_EnumVal_vec_t;
+typedef flatbuffers_uoffset_t *reflection_EnumVal_mutable_vec_t;
+typedef const struct reflection_Enum_table *reflection_Enum_table_t;
+typedef const flatbuffers_uoffset_t *reflection_Enum_vec_t;
+typedef flatbuffers_uoffset_t *reflection_Enum_mutable_vec_t;
+typedef const struct reflection_Field_table *reflection_Field_table_t;
+typedef const flatbuffers_uoffset_t *reflection_Field_vec_t;
+typedef flatbuffers_uoffset_t *reflection_Field_mutable_vec_t;
+typedef const struct reflection_Object_table *reflection_Object_table_t;
+typedef const flatbuffers_uoffset_t *reflection_Object_vec_t;
+typedef flatbuffers_uoffset_t *reflection_Object_mutable_vec_t;
+typedef const struct reflection_Schema_table *reflection_Schema_table_t;
+typedef const flatbuffers_uoffset_t *reflection_Schema_vec_t;
+typedef flatbuffers_uoffset_t *reflection_Schema_mutable_vec_t;
+
+typedef int8_t reflection_BaseType_enum_t;
+__flatbuffers_define_integer_type(reflection_BaseType, reflection_BaseType_enum_t, 8)
+#define reflection_BaseType_None ((reflection_BaseType_enum_t)INT8_C(0))
+#define reflection_BaseType_UType ((reflection_BaseType_enum_t)INT8_C(1))
+#define reflection_BaseType_Bool ((reflection_BaseType_enum_t)INT8_C(2))
+#define reflection_BaseType_Byte ((reflection_BaseType_enum_t)INT8_C(3))
+#define reflection_BaseType_UByte ((reflection_BaseType_enum_t)INT8_C(4))
+#define reflection_BaseType_Short ((reflection_BaseType_enum_t)INT8_C(5))
+#define reflection_BaseType_UShort ((reflection_BaseType_enum_t)INT8_C(6))
+#define reflection_BaseType_Int ((reflection_BaseType_enum_t)INT8_C(7))
+#define reflection_BaseType_UInt ((reflection_BaseType_enum_t)INT8_C(8))
+#define reflection_BaseType_Long ((reflection_BaseType_enum_t)INT8_C(9))
+#define reflection_BaseType_ULong ((reflection_BaseType_enum_t)INT8_C(10))
+#define reflection_BaseType_Float ((reflection_BaseType_enum_t)INT8_C(11))
+#define reflection_BaseType_Double ((reflection_BaseType_enum_t)INT8_C(12))
+#define reflection_BaseType_String ((reflection_BaseType_enum_t)INT8_C(13))
+#define reflection_BaseType_Vector ((reflection_BaseType_enum_t)INT8_C(14))
+#define reflection_BaseType_Obj ((reflection_BaseType_enum_t)INT8_C(15))
+#define reflection_BaseType_Union ((reflection_BaseType_enum_t)INT8_C(16))
+
+static inline const char *reflection_BaseType_name(reflection_BaseType_enum_t value)
+{
+ switch (value) {
+ case reflection_BaseType_None: return "None";
+ case reflection_BaseType_UType: return "UType";
+ case reflection_BaseType_Bool: return "Bool";
+ case reflection_BaseType_Byte: return "Byte";
+ case reflection_BaseType_UByte: return "UByte";
+ case reflection_BaseType_Short: return "Short";
+ case reflection_BaseType_UShort: return "UShort";
+ case reflection_BaseType_Int: return "Int";
+ case reflection_BaseType_UInt: return "UInt";
+ case reflection_BaseType_Long: return "Long";
+ case reflection_BaseType_ULong: return "ULong";
+ case reflection_BaseType_Float: return "Float";
+ case reflection_BaseType_Double: return "Double";
+ case reflection_BaseType_String: return "String";
+ case reflection_BaseType_Vector: return "Vector";
+ case reflection_BaseType_Obj: return "Obj";
+ case reflection_BaseType_Union: return "Union";
+ default: return "";
+ }
+}
+
+
+
+struct reflection_Type_table { uint8_t unused__; };
+
+#ifndef reflection_Type_identifier
+#define reflection_Type_identifier flatbuffers_identifier
+#endif
+#define reflection_Type_type_hash ((flatbuffers_thash_t)0x44c8fe5e)
+#define reflection_Type_type_identifier "\x5e\xfe\xc8\x44"
+static inline size_t reflection_Type_vec_len(reflection_Type_vec_t vec)
+__flatbuffers_vec_len(vec)
+static inline reflection_Type_table_t reflection_Type_vec_at(reflection_Type_vec_t vec, size_t i)
+__flatbuffers_offset_vec_at(reflection_Type_table_t, vec, i, 0)
+__flatbuffers_table_as_root(reflection_Type)
+
+__flatbuffers_define_scalar_field(0, reflection_Type, base_type, reflection_BaseType, reflection_BaseType_enum_t, INT8_C(0))
+__flatbuffers_define_scalar_field(1, reflection_Type, element, reflection_BaseType, reflection_BaseType_enum_t, INT8_C(0))
+__flatbuffers_define_scalar_field(2, reflection_Type, index, flatbuffers_int32, int32_t, INT32_C(-1))
+
+struct reflection_EnumVal_table { uint8_t unused__; };
+
+#ifndef reflection_EnumVal_identifier
+#define reflection_EnumVal_identifier flatbuffers_identifier
+#endif
+#define reflection_EnumVal_type_hash ((flatbuffers_thash_t)0x9531c946)
+#define reflection_EnumVal_type_identifier "\x46\xc9\x31\x95"
+static inline size_t reflection_EnumVal_vec_len(reflection_EnumVal_vec_t vec)
+__flatbuffers_vec_len(vec)
+static inline reflection_EnumVal_table_t reflection_EnumVal_vec_at(reflection_EnumVal_vec_t vec, size_t i)
+__flatbuffers_offset_vec_at(reflection_EnumVal_table_t, vec, i, 0)
+__flatbuffers_table_as_root(reflection_EnumVal)
+
+__flatbuffers_define_string_field(0, reflection_EnumVal, name, 1)
+__flatbuffers_define_scalar_field(1, reflection_EnumVal, value, flatbuffers_int64, int64_t, INT64_C(0))
+/* Note: find only works on vectors sorted by this field. */
+__flatbuffers_define_find_by_scalar_field(reflection_EnumVal, value, int64_t)
+__flatbuffers_define_sort_by_scalar_field(reflection_EnumVal, value, int64_t, flatbuffers_uoffset_t)
+__flatbuffers_define_default_find_by_scalar_field(reflection_EnumVal, value, int64_t)
+__flatbuffers_define_default_scan_by_scalar_field(reflection_EnumVal, value, int64_t)
+#define reflection_EnumVal_vec_sort reflection_EnumVal_vec_sort_by_value
+__flatbuffers_define_table_field(2, reflection_EnumVal, object, reflection_Object_table_t, 0)
+
+struct reflection_Enum_table { uint8_t unused__; };
+
+#ifndef reflection_Enum_identifier
+#define reflection_Enum_identifier flatbuffers_identifier
+#endif
+#define reflection_Enum_type_hash ((flatbuffers_thash_t)0xacffa90f)
+#define reflection_Enum_type_identifier "\x0f\xa9\xff\xac"
+static inline size_t reflection_Enum_vec_len(reflection_Enum_vec_t vec)
+__flatbuffers_vec_len(vec)
+static inline reflection_Enum_table_t reflection_Enum_vec_at(reflection_Enum_vec_t vec, size_t i)
+__flatbuffers_offset_vec_at(reflection_Enum_table_t, vec, i, 0)
+__flatbuffers_table_as_root(reflection_Enum)
+
+__flatbuffers_define_string_field(0, reflection_Enum, name, 1)
+__flatbuffers_define_find_by_string_field(reflection_Enum, name)
+__flatbuffers_define_sort_by_string_field(reflection_Enum, name)
+__flatbuffers_define_default_find_by_string_field(reflection_Enum, name)
+__flatbuffers_define_default_scan_by_string_field(reflection_Enum, name)
+#define reflection_Enum_vec_sort reflection_Enum_vec_sort_by_name
+__flatbuffers_define_vector_field(1, reflection_Enum, values, reflection_EnumVal_vec_t, 1)
+__flatbuffers_define_scalar_field(2, reflection_Enum, is_union, flatbuffers_bool, flatbuffers_bool_t, UINT8_C(0))
+__flatbuffers_define_table_field(3, reflection_Enum, underlying_type, reflection_Type_table_t, 1)
+
+struct reflection_Field_table { uint8_t unused__; };
+
+#ifndef reflection_Field_identifier
+#define reflection_Field_identifier flatbuffers_identifier
+#endif
+#define reflection_Field_type_hash ((flatbuffers_thash_t)0x9f7e408a)
+#define reflection_Field_type_identifier "\x8a\x40\x7e\x9f"
+static inline size_t reflection_Field_vec_len(reflection_Field_vec_t vec)
+__flatbuffers_vec_len(vec)
+static inline reflection_Field_table_t reflection_Field_vec_at(reflection_Field_vec_t vec, size_t i)
+__flatbuffers_offset_vec_at(reflection_Field_table_t, vec, i, 0)
+__flatbuffers_table_as_root(reflection_Field)
+
+__flatbuffers_define_string_field(0, reflection_Field, name, 1)
+__flatbuffers_define_find_by_string_field(reflection_Field, name)
+__flatbuffers_define_sort_by_string_field(reflection_Field, name)
+__flatbuffers_define_default_find_by_string_field(reflection_Field, name)
+__flatbuffers_define_default_scan_by_string_field(reflection_Field, name)
+#define reflection_Field_vec_sort reflection_Field_vec_sort_by_name
+__flatbuffers_define_table_field(1, reflection_Field, type, reflection_Type_table_t, 1)
+__flatbuffers_define_scalar_field(2, reflection_Field, id, flatbuffers_uint16, uint16_t, UINT16_C(0))
+__flatbuffers_define_scalar_field(3, reflection_Field, offset, flatbuffers_uint16, uint16_t, UINT16_C(0))
+__flatbuffers_define_scalar_field(4, reflection_Field, default_integer, flatbuffers_int64, int64_t, INT64_C(0))
+__flatbuffers_define_scalar_field(5, reflection_Field, default_real, flatbuffers_double, double, 0.000000)
+__flatbuffers_define_scalar_field(6, reflection_Field, deprecated, flatbuffers_bool, flatbuffers_bool_t, UINT8_C(0))
+__flatbuffers_define_scalar_field(7, reflection_Field, required, flatbuffers_bool, flatbuffers_bool_t, UINT8_C(0))
+__flatbuffers_define_scalar_field(8, reflection_Field, key, flatbuffers_bool, flatbuffers_bool_t, UINT8_C(0))
+
+struct reflection_Object_table { uint8_t unused__; };
+
+#ifndef reflection_Object_identifier
+#define reflection_Object_identifier flatbuffers_identifier
+#endif
+#define reflection_Object_type_hash ((flatbuffers_thash_t)0xb09729bd)
+#define reflection_Object_type_identifier "\xbd\x29\x97\xb0"
+static inline size_t reflection_Object_vec_len(reflection_Object_vec_t vec)
+__flatbuffers_vec_len(vec)
+static inline reflection_Object_table_t reflection_Object_vec_at(reflection_Object_vec_t vec, size_t i)
+__flatbuffers_offset_vec_at(reflection_Object_table_t, vec, i, 0)
+__flatbuffers_table_as_root(reflection_Object)
+
+__flatbuffers_define_string_field(0, reflection_Object, name, 1)
+__flatbuffers_define_find_by_string_field(reflection_Object, name)
+__flatbuffers_define_sort_by_string_field(reflection_Object, name)
+__flatbuffers_define_default_find_by_string_field(reflection_Object, name)
+__flatbuffers_define_default_scan_by_string_field(reflection_Object, name)
+#define reflection_Object_vec_sort reflection_Object_vec_sort_by_name
+__flatbuffers_define_vector_field(1, reflection_Object, fields, reflection_Field_vec_t, 1)
+__flatbuffers_define_scalar_field(2, reflection_Object, is_struct, flatbuffers_bool, flatbuffers_bool_t, UINT8_C(0))
+__flatbuffers_define_scalar_field(3, reflection_Object, minalign, flatbuffers_int32, int32_t, INT32_C(0))
+__flatbuffers_define_scalar_field(4, reflection_Object, bytesize, flatbuffers_int32, int32_t, INT32_C(0))
+
+struct reflection_Schema_table { uint8_t unused__; };
+
+#ifndef reflection_Schema_identifier
+#define reflection_Schema_identifier flatbuffers_identifier
+#endif
+#define reflection_Schema_type_hash ((flatbuffers_thash_t)0xfaf93779)
+#define reflection_Schema_type_identifier "\x79\x37\xf9\xfa"
+static inline size_t reflection_Schema_vec_len(reflection_Schema_vec_t vec)
+__flatbuffers_vec_len(vec)
+static inline reflection_Schema_table_t reflection_Schema_vec_at(reflection_Schema_vec_t vec, size_t i)
+__flatbuffers_offset_vec_at(reflection_Schema_table_t, vec, i, 0)
+__flatbuffers_table_as_root(reflection_Schema)
+
+__flatbuffers_define_vector_field(0, reflection_Schema, objects, reflection_Object_vec_t, 1)
+__flatbuffers_define_vector_field(1, reflection_Schema, enums, reflection_Enum_vec_t, 1)
+__flatbuffers_define_string_field(2, reflection_Schema, file_ident, 0)
+__flatbuffers_define_string_field(3, reflection_Schema, file_ext, 0)
+__flatbuffers_define_table_field(4, reflection_Schema, root_table, reflection_Object_table_t, 0)
+
+#include "flatcc/portable/pdiagnostic_pop.h"
+#endif /* REFLECTION_READER_H */
diff --git a/Source/library/FBSUtil/flatcc/reflection/reflection_verifier.h b/Source/library/FBSUtil/flatcc/reflection/reflection_verifier.h
new file mode 100644
index 0000000..3ec9d00
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/reflection/reflection_verifier.h
@@ -0,0 +1,206 @@
+#ifndef REFLECTION_VERIFIER_H
+#define REFLECTION_VERIFIER_H
+
+/* Generated by flatcc 0.4.2 FlatBuffers schema compiler for C by dvide.com */
+
+#ifndef REFLECTION_READER_H
+#include "reflection_reader.h"
+#endif
+#include "flatcc/flatcc_verifier.h"
+#define PDIAGNOSTIC_IGNORE_UNUSED
+#include "flatcc/portable/pdiagnostic_push.h"
+
+static int __reflection_Type_table_verifier(flatcc_table_verifier_descriptor_t *td);
+static int __reflection_EnumVal_table_verifier(flatcc_table_verifier_descriptor_t *td);
+static int __reflection_Enum_table_verifier(flatcc_table_verifier_descriptor_t *td);
+static int __reflection_Field_table_verifier(flatcc_table_verifier_descriptor_t *td);
+static int __reflection_Object_table_verifier(flatcc_table_verifier_descriptor_t *td);
+static int __reflection_Schema_table_verifier(flatcc_table_verifier_descriptor_t *td);
+
+static int __reflection_Type_table_verifier(flatcc_table_verifier_descriptor_t *td)
+{
+ int ret;
+ if ((ret = flatcc_verify_field(td, 0, 1, 1) /* base_type */)) return ret;
+ if ((ret = flatcc_verify_field(td, 1, 1, 1) /* element */)) return ret;
+ if ((ret = flatcc_verify_field(td, 2, 4, 4) /* index */)) return ret;
+ return flatcc_verify_ok;
+}
+
+static inline int reflection_Type_verify_as_root(const void *buf, size_t bufsiz)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, reflection_Type_identifier, &__reflection_Type_table_verifier);
+}
+
+static inline int reflection_Type_verify_as_typed_root(const void *buf, size_t bufsiz)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, reflection_Type_type_identifier, &__reflection_Type_table_verifier);
+}
+
+static inline int reflection_Type_verify_as_root_with_identifier(const void *buf, size_t bufsiz, const char *fid)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, fid, &__reflection_Type_table_verifier);
+}
+
+static inline int reflection_Type_verify_as_root_with_type_hash(const void *buf, size_t bufsiz, flatbuffers_thash_t thash)
+{
+ return flatcc_verify_table_as_typed_root(buf, bufsiz, thash, &__reflection_Type_table_verifier);
+}
+
+static int __reflection_EnumVal_table_verifier(flatcc_table_verifier_descriptor_t *td)
+{
+ int ret;
+ if ((ret = flatcc_verify_string_field(td, 0, 1) /* name */)) return ret;
+ if ((ret = flatcc_verify_field(td, 1, 8, 8) /* value */)) return ret;
+ if ((ret = flatcc_verify_table_field(td, 2, 0, &__reflection_Object_table_verifier) /* object */)) return ret;
+ return flatcc_verify_ok;
+}
+
+static inline int reflection_EnumVal_verify_as_root(const void *buf, size_t bufsiz)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, reflection_EnumVal_identifier, &__reflection_EnumVal_table_verifier);
+}
+
+static inline int reflection_EnumVal_verify_as_typed_root(const void *buf, size_t bufsiz)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, reflection_EnumVal_type_identifier, &__reflection_EnumVal_table_verifier);
+}
+
+static inline int reflection_EnumVal_verify_as_root_with_identifier(const void *buf, size_t bufsiz, const char *fid)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, fid, &__reflection_EnumVal_table_verifier);
+}
+
+static inline int reflection_EnumVal_verify_as_root_with_type_hash(const void *buf, size_t bufsiz, flatbuffers_thash_t thash)
+{
+ return flatcc_verify_table_as_typed_root(buf, bufsiz, thash, &__reflection_EnumVal_table_verifier);
+}
+
+static int __reflection_Enum_table_verifier(flatcc_table_verifier_descriptor_t *td)
+{
+ int ret;
+ if ((ret = flatcc_verify_string_field(td, 0, 1) /* name */)) return ret;
+ if ((ret = flatcc_verify_table_vector_field(td, 1, 1, &__reflection_EnumVal_table_verifier) /* values */)) return ret;
+ if ((ret = flatcc_verify_field(td, 2, 1, 1) /* is_union */)) return ret;
+ if ((ret = flatcc_verify_table_field(td, 3, 1, &__reflection_Type_table_verifier) /* underlying_type */)) return ret;
+ return flatcc_verify_ok;
+}
+
+static inline int reflection_Enum_verify_as_root(const void *buf, size_t bufsiz)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, reflection_Enum_identifier, &__reflection_Enum_table_verifier);
+}
+
+static inline int reflection_Enum_verify_as_typed_root(const void *buf, size_t bufsiz)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, reflection_Enum_type_identifier, &__reflection_Enum_table_verifier);
+}
+
+static inline int reflection_Enum_verify_as_root_with_identifier(const void *buf, size_t bufsiz, const char *fid)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, fid, &__reflection_Enum_table_verifier);
+}
+
+static inline int reflection_Enum_verify_as_root_with_type_hash(const void *buf, size_t bufsiz, flatbuffers_thash_t thash)
+{
+ return flatcc_verify_table_as_typed_root(buf, bufsiz, thash, &__reflection_Enum_table_verifier);
+}
+
+static int __reflection_Field_table_verifier(flatcc_table_verifier_descriptor_t *td)
+{
+ int ret;
+ if ((ret = flatcc_verify_string_field(td, 0, 1) /* name */)) return ret;
+ if ((ret = flatcc_verify_table_field(td, 1, 1, &__reflection_Type_table_verifier) /* type */)) return ret;
+ if ((ret = flatcc_verify_field(td, 2, 2, 2) /* id */)) return ret;
+ if ((ret = flatcc_verify_field(td, 3, 2, 2) /* offset */)) return ret;
+ if ((ret = flatcc_verify_field(td, 4, 8, 8) /* default_integer */)) return ret;
+ if ((ret = flatcc_verify_field(td, 5, 8, 8) /* default_real */)) return ret;
+ if ((ret = flatcc_verify_field(td, 6, 1, 1) /* deprecated */)) return ret;
+ if ((ret = flatcc_verify_field(td, 7, 1, 1) /* required */)) return ret;
+ if ((ret = flatcc_verify_field(td, 8, 1, 1) /* key */)) return ret;
+ return flatcc_verify_ok;
+}
+
+static inline int reflection_Field_verify_as_root(const void *buf, size_t bufsiz)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, reflection_Field_identifier, &__reflection_Field_table_verifier);
+}
+
+static inline int reflection_Field_verify_as_typed_root(const void *buf, size_t bufsiz)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, reflection_Field_type_identifier, &__reflection_Field_table_verifier);
+}
+
+static inline int reflection_Field_verify_as_root_with_identifier(const void *buf, size_t bufsiz, const char *fid)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, fid, &__reflection_Field_table_verifier);
+}
+
+static inline int reflection_Field_verify_as_root_with_type_hash(const void *buf, size_t bufsiz, flatbuffers_thash_t thash)
+{
+ return flatcc_verify_table_as_typed_root(buf, bufsiz, thash, &__reflection_Field_table_verifier);
+}
+
+static int __reflection_Object_table_verifier(flatcc_table_verifier_descriptor_t *td)
+{
+ int ret;
+ if ((ret = flatcc_verify_string_field(td, 0, 1) /* name */)) return ret;
+ if ((ret = flatcc_verify_table_vector_field(td, 1, 1, &__reflection_Field_table_verifier) /* fields */)) return ret;
+ if ((ret = flatcc_verify_field(td, 2, 1, 1) /* is_struct */)) return ret;
+ if ((ret = flatcc_verify_field(td, 3, 4, 4) /* minalign */)) return ret;
+ if ((ret = flatcc_verify_field(td, 4, 4, 4) /* bytesize */)) return ret;
+ return flatcc_verify_ok;
+}
+
+static inline int reflection_Object_verify_as_root(const void *buf, size_t bufsiz)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, reflection_Object_identifier, &__reflection_Object_table_verifier);
+}
+
+static inline int reflection_Object_verify_as_typed_root(const void *buf, size_t bufsiz)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, reflection_Object_type_identifier, &__reflection_Object_table_verifier);
+}
+
+static inline int reflection_Object_verify_as_root_with_identifier(const void *buf, size_t bufsiz, const char *fid)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, fid, &__reflection_Object_table_verifier);
+}
+
+static inline int reflection_Object_verify_as_root_with_type_hash(const void *buf, size_t bufsiz, flatbuffers_thash_t thash)
+{
+ return flatcc_verify_table_as_typed_root(buf, bufsiz, thash, &__reflection_Object_table_verifier);
+}
+
+static int __reflection_Schema_table_verifier(flatcc_table_verifier_descriptor_t *td)
+{
+ int ret;
+ if ((ret = flatcc_verify_table_vector_field(td, 0, 1, &__reflection_Object_table_verifier) /* objects */)) return ret;
+ if ((ret = flatcc_verify_table_vector_field(td, 1, 1, &__reflection_Enum_table_verifier) /* enums */)) return ret;
+ if ((ret = flatcc_verify_string_field(td, 2, 0) /* file_ident */)) return ret;
+ if ((ret = flatcc_verify_string_field(td, 3, 0) /* file_ext */)) return ret;
+ if ((ret = flatcc_verify_table_field(td, 4, 0, &__reflection_Object_table_verifier) /* root_table */)) return ret;
+ return flatcc_verify_ok;
+}
+
+static inline int reflection_Schema_verify_as_root(const void *buf, size_t bufsiz)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, reflection_Schema_identifier, &__reflection_Schema_table_verifier);
+}
+
+static inline int reflection_Schema_verify_as_typed_root(const void *buf, size_t bufsiz)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, reflection_Schema_type_identifier, &__reflection_Schema_table_verifier);
+}
+
+static inline int reflection_Schema_verify_as_root_with_identifier(const void *buf, size_t bufsiz, const char *fid)
+{
+ return flatcc_verify_table_as_root(buf, bufsiz, fid, &__reflection_Schema_table_verifier);
+}
+
+static inline int reflection_Schema_verify_as_root_with_type_hash(const void *buf, size_t bufsiz, flatbuffers_thash_t thash)
+{
+ return flatcc_verify_table_as_typed_root(buf, bufsiz, thash, &__reflection_Schema_table_verifier);
+}
+
+#include "flatcc/portable/pdiagnostic_pop.h"
+#endif /* REFLECTION_VERIFIER_H */
diff --git a/Source/library/FBSUtil/flatcc/support/README b/Source/library/FBSUtil/flatcc/support/README
new file mode 100644
index 0000000..d9f6ec0
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/support/README
@@ -0,0 +1 @@
+support files mainly used for testing
diff --git a/Source/library/FBSUtil/flatcc/support/cdump.h b/Source/library/FBSUtil/flatcc/support/cdump.h
new file mode 100644
index 0000000..56d48a0
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/support/cdump.h
@@ -0,0 +1,31 @@
+#ifndef CDUMP_H
+#define CDUMP_H
+
+#include
+
+/* Generates a constant a C byte array. */
+static void cdump(char *name, void *addr, size_t len, FILE *fp) {
+ unsigned int i;
+ unsigned char buff[17];
+ unsigned char *pc = (unsigned char*)addr;
+
+ // Output description if given.
+ name = name ? name : "dump";
+ fprintf(fp, "const unsigned char %s[] = {", name);
+
+ // Process every byte in the data.
+ for (i = 0; i < (unsigned int)len; i++) {
+ // Multiple of 16 means new line (with line offset).
+
+ if ((i % 16) == 0) {
+ fprintf(fp, "\n ");
+ } else if ((i % 8) == 0) {
+ fprintf(fp, " ");
+ }
+
+ fprintf(fp, " 0x%02x,", pc[i]);
+ }
+ fprintf(fp, "\n};\n");
+}
+
+#endif /* CDUMP_H */
diff --git a/Source/library/FBSUtil/flatcc/support/elapsed.h b/Source/library/FBSUtil/flatcc/support/elapsed.h
new file mode 100644
index 0000000..f89663a
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/support/elapsed.h
@@ -0,0 +1,65 @@
+#ifndef ELAPSED_H
+#define ELAPSED_H
+
+#include
+
+/* Based on http://stackoverflow.com/a/8583395 */
+#if !defined(_WIN32)
+#include
+static double elapsed_realtime(void) { // returns 0 seconds first time called
+ static struct timeval t0;
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ if (!t0.tv_sec)
+ t0 = tv;
+ return tv.tv_sec - t0.tv_sec + (tv.tv_usec - t0.tv_usec) / 1000000.;
+}
+#else
+#include
+#ifndef FatalError
+#define FatalError(s) do { perror(s); exit(-1); } while(0)
+#endif
+static double elapsed_realtime(void) { // granularity about 50 microsecs on my machine
+ static LARGE_INTEGER freq, start;
+ LARGE_INTEGER count;
+ if (!QueryPerformanceCounter(&count))
+ FatalError("QueryPerformanceCounter");
+ if (!freq.QuadPart) { // one time initialization
+ if (!QueryPerformanceFrequency(&freq))
+ FatalError("QueryPerformanceFrequency");
+ start = count;
+ }
+ return (double)(count.QuadPart - start.QuadPart) / freq.QuadPart;
+}
+#endif
+
+/* end Based on stackoverflow */
+
+static int show_benchmark(const char *descr, double t1, double t2, size_t size, int rep, const char *reptext)
+{
+ double tdiff = t2 - t1;
+ double nstime;
+
+ printf("operation: %s\n", descr);
+ printf("elapsed time: %.3f (s)\n", tdiff);
+ printf("iterations: %d\n", rep);
+ printf("size: %lu (bytes)\n", (unsigned long)size);
+ printf("bandwidth: %.3f (MB/s)\n", (double)rep * size / 1e6 / tdiff);
+ printf("throughput in ops per sec: %.3f\n", rep / tdiff);
+ if (reptext && rep != 1) {
+ printf("throughput in %s ops per sec: %.3f\n", reptext, 1 / tdiff);
+ }
+ nstime = tdiff * 1e9 / rep;
+ if (nstime < 1000) {
+ printf("time per op: %.3f (ns)\n", nstime);
+ } else if (nstime < 1e6) {
+ printf("time per op: %.3f (us)\n", nstime / 1000);
+ } else if (nstime < 1e9) {
+ printf("time per op: %.3f (ms)\n", nstime / 1e6);
+ } else {
+ printf("time per op: %.3f (s)\n", nstime / 1e9);
+ }
+ return 0;
+}
+
+#endif /* ELAPSED_H */
diff --git a/Source/library/FBSUtil/flatcc/support/hexdump.h b/Source/library/FBSUtil/flatcc/support/hexdump.h
new file mode 100644
index 0000000..430ec68
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/support/hexdump.h
@@ -0,0 +1,52 @@
+#ifndef HEXDUMP_H
+#define HEXDUMP_H
+
+#include
+
+/* Based on: http://stackoverflow.com/a/7776146 */
+static void hexdump(char *desc, void *addr, size_t len, FILE *fp) {
+ unsigned int i;
+ unsigned char buff[17];
+ unsigned char *pc = (unsigned char*)addr;
+
+ // Output description if given.
+ if (desc != NULL)
+ fprintf(fp, "%s:\n", desc);
+
+ // Process every byte in the data.
+ for (i = 0; i < (unsigned int)len; i++) {
+ // Multiple of 16 means new line (with line offset).
+
+ if ((i % 16) == 0) {
+ // Just don't print ASCII for the zeroth line.
+ if (i != 0)
+ fprintf(fp, " %s\n", buff);
+
+ // Output the offset.
+ fprintf(fp, " %04x ", i);
+ } else if ((i % 8) == 0) {
+ fprintf(fp, " ");
+ }
+
+ // Now the hex code for the specific character.
+ fprintf(fp, " %02x", pc[i]);
+
+ // And store a printable ASCII character for later.
+ if ((pc[i] < 0x20) || (pc[i] > 0x7e))
+ buff[i % 16] = '.';
+ else
+ buff[i % 16] = pc[i];
+ buff[(i % 16) + 1] = '\0';
+ }
+
+ // Pad out last line if not exactly 16 characters.
+ while ((i % 16) != 0) {
+ fprintf(fp, " ");
+ i++;
+ }
+
+ // And print the final ASCII bit.
+ fprintf(fp, " %s\n", buff);
+}
+
+#endif /* HEXDUMP_H */
diff --git a/Source/library/FBSUtil/flatcc/support/readfile.h b/Source/library/FBSUtil/flatcc/support/readfile.h
new file mode 100644
index 0000000..680252b
--- /dev/null
+++ b/Source/library/FBSUtil/flatcc/support/readfile.h
@@ -0,0 +1,55 @@
+#ifndef READFILE_H
+#define READFILE_H
+
+#include
+#include
+
+static char *readfile(const char *filename, size_t max_size, size_t *size_out)
+{
+ FILE *fp;
+ size_t size, pos, n, _out;
+ char *buf;
+
+ size_out = size_out ? size_out : &_out;
+
+ fp = fopen(filename, "rb");
+ size = 0;
+ buf = 0;
+
+ if (!fp) {
+ goto fail;
+ }
+ fseek(fp, 0L, SEEK_END);
+ size = ftell(fp);
+ *size_out = size;
+ if (max_size > 0 && size > max_size) {
+ goto fail;
+ }
+ rewind(fp);
+ buf = malloc(size ? size : 1);
+ if (!buf) {
+ goto fail;
+ }
+ pos = 0;
+ while ((n = fread(buf + pos, 1, size - pos, fp))) {
+ pos += n;
+ }
+ if (pos != size) {
+ goto fail;
+ }
+ fclose(fp);
+ *size_out = size;
+ return buf;
+
+fail:
+ if (fp) {
+ fclose(fp);
+ }
+ if (buf) {
+ free(buf);
+ }
+ *size_out = size;
+ return 0;
+}
+
+#endif /* READFILE_H */
diff --git a/Source/library/FBSUtil/module.modulemap b/Source/library/FBSUtil/module.modulemap
new file mode 100644
index 0000000..f7daa11
--- /dev/null
+++ b/Source/library/FBSUtil/module.modulemap
@@ -0,0 +1,4 @@
+module LibFBSUtil [system][extern_c] {
+ header "FBSUtil.h"
+ export *
+}
diff --git a/Source/library/README.md b/Source/library/README.md
new file mode 100644
index 0000000..0a79d11
--- /dev/null
+++ b/Source/library/README.md
@@ -0,0 +1,4 @@
+# C Library
+This folder contains C modules used in Serrano.
+
+- FBSUtil: A C library based on [flatcc](https://github.com/dvidelabs/flatcc) offering APIs read/write flatbuffers.
\ No newline at end of file
diff --git a/Tests/MacTestHostingApp/AppDelegate.swift b/Tests/MacTestHostingApp/AppDelegate.swift
new file mode 100644
index 0000000..63d5be2
--- /dev/null
+++ b/Tests/MacTestHostingApp/AppDelegate.swift
@@ -0,0 +1,26 @@
+//
+// AppDelegate.swift
+// MacTestHostingApp
+//
+// Created by ZHONGHAO LIU on 8/15/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Cocoa
+
+@NSApplicationMain
+class AppDelegate: NSObject, NSApplicationDelegate {
+
+
+
+ func applicationDidFinishLaunching(_ aNotification: Notification) {
+ // Insert code here to initialize your application
+ }
+
+ func applicationWillTerminate(_ aNotification: Notification) {
+ // Insert code here to tear down your application
+ }
+
+
+}
+
diff --git a/Tests/MacTestHostingApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/Tests/MacTestHostingApp/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..2db2b1c
--- /dev/null
+++ b/Tests/MacTestHostingApp/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,58 @@
+{
+ "images" : [
+ {
+ "idiom" : "mac",
+ "size" : "16x16",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "16x16",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "32x32",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "32x32",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "128x128",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "128x128",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "256x256",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "256x256",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "512x512",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "512x512",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Tests/MacTestHostingApp/Base.lproj/Main.storyboard b/Tests/MacTestHostingApp/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..5e7e3a6
--- /dev/null
+++ b/Tests/MacTestHostingApp/Base.lproj/Main.storyboard
@@ -0,0 +1,706 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/MacTestHostingApp/Info.plist b/Tests/MacTestHostingApp/Info.plist
new file mode 100644
index 0000000..f219fe5
--- /dev/null
+++ b/Tests/MacTestHostingApp/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+ LSMinimumSystemVersion
+ $(MACOSX_DEPLOYMENT_TARGET)
+ NSHumanReadableCopyright
+ Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+ NSMainStoryboardFile
+ Main
+ NSPrincipalClass
+ NSApplication
+
+
diff --git a/Tests/MacTestHostingApp/ViewController.swift b/Tests/MacTestHostingApp/ViewController.swift
new file mode 100644
index 0000000..8f01a25
--- /dev/null
+++ b/Tests/MacTestHostingApp/ViewController.swift
@@ -0,0 +1,27 @@
+//
+// ViewController.swift
+// MacTestHostingApp
+//
+// Created by ZHONGHAO LIU on 8/15/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import Cocoa
+
+class ViewController: NSViewController {
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ // Do any additional setup after loading the view.
+ }
+
+ override var representedObject: Any? {
+ didSet {
+ // Update the view, if already loaded.
+ }
+ }
+
+
+}
+
diff --git a/Tests/README.md b/Tests/README.md
new file mode 100644
index 0000000..c1c9557
--- /dev/null
+++ b/Tests/README.md
@@ -0,0 +1 @@
+Since Metal library is not included in Xcode simulator. We need create a Host Applciation so that we can do Metal related Uit testing on iOS device.
\ No newline at end of file
diff --git a/Tests/SerranoMacTests/Info.plist b/Tests/SerranoMacTests/Info.plist
new file mode 100644
index 0000000..6c6c23c
--- /dev/null
+++ b/Tests/SerranoMacTests/Info.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ BNDL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+
+
diff --git a/Tests/SerranoMacTests/SerranoMacTests.swift b/Tests/SerranoMacTests/SerranoMacTests.swift
new file mode 100644
index 0000000..caa8988
--- /dev/null
+++ b/Tests/SerranoMacTests/SerranoMacTests.swift
@@ -0,0 +1,35 @@
+//
+// SerranoMacTests.swift
+// SerranoMacTests
+//
+// Created by ZHONGHAO LIU on 8/15/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+
+class SerranoMacTests: XCTestCase {
+
+ override func setUp() {
+ super.setUp()
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ }
+
+ override func tearDown() {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ super.tearDown()
+ }
+
+ func testExample() {
+ // This is an example of a functional test case.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+ }
+
+ func testPerformanceExample() {
+ // This is an example of a performance test case.
+ self.measure {
+ // Put the code you want to measure the time of here.
+ }
+ }
+
+}
diff --git a/Tests/SerranoTests/Supporting Files/Info.plist b/Tests/SerranoTests/Supporting Files/Info.plist
new file mode 100644
index 0000000..6c6c23c
--- /dev/null
+++ b/Tests/SerranoTests/Supporting Files/Info.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ BNDL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+
+
diff --git a/Tests/SerranoTests/core/engine_test.swift b/Tests/SerranoTests/core/engine_test.swift
new file mode 100644
index 0000000..9819cf3
--- /dev/null
+++ b/Tests/SerranoTests/core/engine_test.swift
@@ -0,0 +1,123 @@
+//
+// engine_test.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 3/23/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+import Metal
+@testable import Serrano
+
+class engine_test: XCTestCase {
+
+ override func setUp() {
+ super.setUp()
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ }
+
+ override func tearDown() {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ super.tearDown()
+ }
+
+// func testExample() {
+// // This is an example of a functional test case.
+// // Use XCTAssert and related functions to verify your tests produce the correct results.
+//
+//// test_configure_engine()
+//// testLoadingKernel()
+//// testLoadingKernel()
+// }
+
+// func testPerformanceExample() {
+// // This is an example of a performance test case.
+// self.measure {
+// // Put the code you want to measure the time of here.
+// }
+// }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ Test function: configureEngine()
+
+ - Note: This test needs to be running on physical iOS device with METAL support.
+ */
+ func test_configure_GPUEngine_default() {
+ let engine = SerranoEngine.configuredEngine
+ engine.resetEngine()
+ let (success, msg) = engine.configureEngine(computationMode: .GPU, serranoCommandQueue: nil, serranoMTLLibrary: nil, systemDefaultGPUDevice: nil)
+ if !success {
+ print("No GPU available. GIVE up test")
+ return
+ }
+ XCTAssertTrue(success, "Fail test:\(msg)")
+
+ // attributes
+ XCTAssertNotNil(engine.GPUDevice)
+ XCTAssertNotNil(engine.metalLibrary)
+ XCTAssertNotNil(engine.serranoCommandQueue)
+ }
+
+ func test_configure_GPUEngine_fromUserParams() {
+ let engine = SerranoEngine.configuredEngine
+ engine.resetEngine()
+ let GPUDevice = MTLCreateSystemDefaultDevice()
+ guard GPUDevice != nil else {
+ print("NO gpu available. Give up test")
+ return
+ }
+
+ let (success, msg) = engine.configureEngine(computationMode: .GPU, serranoCommandQueue: nil, serranoMTLLibrary: nil, systemDefaultGPUDevice: GPUDevice!)
+ XCTAssertTrue(success, "Fail test:\(msg)")
+
+ // attributes
+ XCTAssertNotNil(engine.GPUDevice)
+ XCTAssertNotNil(engine.metalLibrary)
+ XCTAssertNotNil(engine.serranoCommandQueue)
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ Target:
+ public func loadGPUKernel(kernelLabel label: String) -> (result: MTLComputePipelineState?, message: String)
+ */
+ func test_loading_kernels() {
+ let engine = SerranoEngine.configuredEngine
+ engine.resetEngine()
+ let (success, msg) = engine.configureEngine(computationMode: .GPU, serranoCommandQueue: nil, serranoMTLLibrary: nil, systemDefaultGPUDevice: nil)
+ if !success {
+ print("No gpu available. Give up test")
+ return
+ }
+
+ // precondition
+ XCTAssertTrue(success, "Fail test:\(msg)")
+ XCTAssertNotNil(engine.metalLibrary)
+
+ // All functions in serrano's default metal library should be successfully load
+ print("\n\nTesting function loading....")
+ for funcName in engine.metalLibrary!.functionNames {
+ let (kernel, msg) = engine.loadGPUKernel(kernelLabel: funcName)
+ XCTAssertNotNil(kernel, "Fail test: \(msg)")
+ print("Pass! Successfully Load kernel \(funcName)")
+ }
+
+
+ // Randomly generate function name
+ print("\n\nTesting random function loading....")
+ for i in 0..<10 {
+ let randomFuncName = randomString(length: i+1)
+ let (kernel, msg) = engine.loadGPUKernel(kernelLabel: randomFuncName)
+ XCTAssertNil(kernel, "Fail test: \(msg)")
+ print("Pass! Random function name: \(randomFuncName)")
+
+ }
+
+ }
+
+
+}
diff --git a/Tests/SerranoTests/core/resource_manager_test.swift b/Tests/SerranoTests/core/resource_manager_test.swift
new file mode 100644
index 0000000..71f2083
--- /dev/null
+++ b/Tests/SerranoTests/core/resource_manager_test.swift
@@ -0,0 +1,108 @@
+//
+// resource_manager_test.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 8/9/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class resource_manager_test: XCTestCase {
+
+ override func setUp() {
+ super.setUp()
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ }
+
+ override func tearDown() {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ super.tearDown()
+ }
+
+ /**
+ Target:
+ public func allocateTensors(forShapes shapes: [TensorShape]) -> [Tensor]
+ public func returnTensors(_ tensors: [Tensor])
+ public func isManagingTensor(_ tensor: Tensor) -> Bool
+ public func isTensorAvailable(_ tensor: Tensor) -> Bool
+ */
+ func testTensorManagement() {
+ let numCase = 100
+ let manager = SerranoResourceManager()
+
+ for i in 0...stride
+ while shape2NeedBytes > tensor1AllocateBytes {
+ shape2 = randomShape(dimensions: 2, dimensionSizeRange: [40, 50], dataType: .float)
+ shape2NeedBytes = shape2.shapeArray.reduce(1, *) * MemoryLayout.stride
+ }
+ print("Tensor 2 require bytes: \(shape2NeedBytes), tensor 1 allocate bytes: \(tensor1AllocateBytes)")
+ } else {
+ // generate shape2 should be larger than shape1
+ var shape2NeedBytes = shape2.shapeArray.reduce(1, *) * MemoryLayout.stride
+ while shape2NeedBytes <= tensor1AllocateBytes {
+ shape2 = randomShape(dimensions: 2, dimensionSizeRange: [200, 210], dataType: .float)
+ shape2NeedBytes = shape2.shapeArray.reduce(1, *) * MemoryLayout.stride
+ }
+ print("Tensor 2 require bytes: \(shape2NeedBytes), tensor 1 allocate bytes: \(tensor1AllocateBytes)")
+ }
+
+
+ let newTensor2 = manager.allocateTensors([shape2]).first
+ XCTAssertNotNil(newTensor2)
+ XCTAssertTrue(newTensor2!.shape == shape2)
+ XCTAssertTrue(manager.isManagingTensor(newTensor2!))
+ XCTAssertTrue(!manager.isTensorAvailable(newTensor2!))
+
+ if i % 2 == 0 {
+ // should reuse
+ XCTAssertTrue(!manager.isTensorAvailable(newTensor!))
+ XCTAssertTrue(newTensor2! == newTensor!)
+ } else {
+ // should allocate a new tensor
+ XCTAssertTrue(manager.isTensorAvailable(newTensor!))
+ XCTAssertTrue(newTensor2 != newTensor!)
+ print(newTensor!.description)
+ print(newTensor2!.description)
+ }
+
+
+ manager.releaseAllResources()
+
+ print("Finish Test \(i+1)\n\n\n")
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ Target:
+ public func allocateMTLBuffers(forTensors tensors: [Tensor]) -> [MTLBuffer]
+ public func releaseTensorAttachedBuffers(_ tensors: [Tensor])
+ public func isManagingBufferr(_ buffer: MTLBuffer) -> Bool
+ */
+ func testBufferManagement() {
+ // TODO:
+ }
+
+}
diff --git a/Tests/SerranoTests/core/tensor_test.swift b/Tests/SerranoTests/core/tensor_test.swift
new file mode 100644
index 0000000..2b17612
--- /dev/null
+++ b/Tests/SerranoTests/core/tensor_test.swift
@@ -0,0 +1,811 @@
+//
+// tensor_test.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 3/16/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+import Metal
+@testable import Serrano
+
+class TensorTest: XCTestCase {
+
+ override func setUp() {
+ super.setUp()
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ }
+
+ override func tearDown() {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ super.tearDown()
+ }
+
+// func testExample() {
+// // This is an example of a functional test case.
+// // Use XCTAssert and related functions to verify your tests produce the correct results.
+//
+// }
+//
+// func testPerformanceExample() {
+// // This is an example of a performance test case.
+// self.measure {
+// // Put the code you want to measure the time of here.
+// }
+// }
+
+
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+ /**
+ Targets:
+ Tensor.init(dataArray array:[Any], tensorShape shape: TensorShape)
+ flatArrayFloat() -> [Float]
+ */
+ func testCreateTensor() {
+ let numCase = 100
+ //TODO: more test
+ for _ in 0.. [Any]
+ func constructNestedArrayFrom(location: [Int]) -> [Any]
+ */
+ func testNestedFunc() {
+ let numCase = 100
+
+ for _ in 0.. [Int] {
+ var indices = [Int]()
+ for dimSize in shape {
+ if valid {
+ indices.append(randomInt([0, dimSize-1]))
+ } else {
+ indices.append(randomInt([-1, dimSize+3]))
+ }
+ }
+ return indices
+ }
+
+ /**
+ Target:
+ func indexIsValid(_ index: [Int]) -> Bool
+ */
+ func testIndexValid() {
+ let numCase = 100
+
+ for _ in 0.. Float
+ */
+ func testSubscript() {
+ let numCase = 100
+
+ for _ in 0..(OpaquePointer(bufferAddress))
+ let mtlBufferReader = UnsafeMutableBufferPointer(start: mtlBufferPointer, count: tensor.capacity)
+ for index in 0.. Tensor?
+ */
+ func testTensorBatchSlice() {
+ let numCase = 100
+ for i in 0.. ComputationGraph {
+ let g = ComputationGraph()
+
+ // input tensors
+ var dataTensoSymbols = [TensorSymbol]()
+ let shape = TensorShape(dataType: .float, shape: [randomInt([100, 100]), randomInt([100, 100]), 3])
+ for _ in 0.. TensorSymbol
+ */
+ func testTensor() {
+ let numCase = 100
+ let graph = ComputationGraph()
+
+ for i in 0.. ScalarSymbol
+ */
+ func testScalar() {
+ let numCase = 100
+ let graph = ComputationGraph()
+
+ for i in 0.. ([TensorSymbol], OperatorSymbol)
+ */
+ func testOperation() {
+ let numCase = 100
+ let graph = ComputationGraph()
+
+ for i in 0.. Bool in
+ symbol.UID == tensorSymbol.UID
+ }))
+ XCTAssertTrue(tensorSymbol.outBounds.contains(where: { (symbol) -> Bool in
+ symbol.UID == opSymbol.UID
+ }))
+ }
+
+ // check bounds between outSymbol && opSymbol
+ for tensorSymbol in outSymbols {
+ XCTAssertTrue(opSymbol.outBounds.contains(where: { (symbol) -> Bool in
+ symbol.UID == tensorSymbol.UID
+ }))
+ XCTAssertTrue(tensorSymbol.inBounds.contains(where: { (symbol) -> Bool in
+ symbol.UID == opSymbol.UID
+ }))
+ }
+
+ // check bounds between opSymbol and parameter symbol if available
+ if i % 3 == 2 {
+ XCTAssertTrue(opSymbol.paramSymbols.count > 0)
+ for paramSymbol in opSymbol.paramSymbols {
+ XCTAssertTrue(opSymbol.inBounds.contains(where: { (symbol) -> Bool in
+ symbol.UID == paramSymbol.UID
+ }))
+ XCTAssertTrue(paramSymbol.outBounds.contains(where: { (symbol) -> Bool in
+ symbol.UID == opSymbol.UID
+ }))
+ }
+ }
+
+ print("Finish test case \(i+1)\n\n")
+ }
+
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ Target:
+ public func sortGraph()
+ */
+ func testSortGraph() {
+ let numCase = 100
+ for i in 0.. (valid: Bool, msg: String)
+ */
+ func testUserInputBindCheck() {
+ let numCase = 100
+ for i in 0.. (valid: Bool, msg: String)
+ internal func checkShapeChain() -> (valid: Bool, msg: String)
+ */
+ func testVerifyGraph() {
+ let numCase = 50
+ for i in 0.. [Tensor]?
+ internal func stageOrderCalculate(mode: OperatorComputationMode)
+ */
+ func testForward() {
+ let numCase = 50
+
+ let _ = SerranoEngine.configuredEngine.configureEngine(computationMode: .GPU)
+
+ for i in 0.. Graph in extension
+ */
+ func testGraphGenerate() {
+ //TODO: test
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ Target:
+ public func serranoSymbolUIDGenerate() -> String
+ */
+ func testSerranoSymbolUIDGenerate() {
+ let numCase = 10000
+ var UIDSet = Set()
+
+ for i in 0.. Bool in
+ return symbol.UID == inputA.UID
+ }))
+ XCTAssertTrue(opSymbol.inputSymbols.contains(where: { (symbol) -> Bool in
+ return symbol.UID == inputB.UID
+ }))
+
+ // inbouds
+ XCTAssertTrue(opSymbol.inBounds.contains(where: { (symbol) -> Bool in
+ return symbol.UID == inputA.UID
+ }))
+ XCTAssertTrue(opSymbol.inBounds.contains(where: { (symbol) -> Bool in
+ return symbol.UID == inputB.UID
+ }))
+
+ print("Finish test case \(i+1)\n\n")
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ Target:
+ public func outputSymbols() -> [TensorSymbol]
+ */
+ func testOutputSymbols() {
+ let numCase = 100
+ for i in 0..(inputSymbols[0..<2]))
+ outputSymbols = opSymbol.outputSymbols()
+ }
+
+ // get output symbols checking
+ var outputSymbolsCheck = [TensorSymbol]()
+ let inputShapes = opSymbol.inputSymbols.map({ (symbol) -> TensorShape in
+ return symbol.shape
+ })
+ let outputShapes = opSymbol.serranoOperator.outputShape(shapeArray: inputShapes)
+ for outputShape in outputShapes! {
+ outputSymbolsCheck.append(SerranoTensorSymbol(dataSource: .User, shape: outputShape))
+ }
+
+ // CHECK
+ XCTAssertTrue(outputSymbolsCheck.count == outputSymbols.count)
+ for (outSymbolCheck, outSymbol) in zip(outputSymbolsCheck, outputSymbols) {
+ print("Out symbol check:", outSymbolCheck.shape.description)
+ print("Out symbol:", outSymbol.shape.description)
+ XCTAssertTrue(outSymbolCheck.shape == outSymbol.shape)
+ }
+
+ print("Finish test case \(i+1)\n\n")
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ Target:
+ public func addToParamSymbols(_ symbol: GraphSymbol)
+ */
+ func testAddToParamSymbols() {
+ let numCase = 100
+ for i in 0.. Bool
+ */
+ func testBindData() {
+ let numCase = 100
+ for i in 0.. Void)?) {
+ let blcok = {(inputTensors: [Tensor], resultTensor: Tensor) -> Void in
+ let inputReaderA = inputTensors[0].floatValueReader
+ let inputReaderB = inputTensors[1].floatValueReader
+ let resultReader = resultTensor.floatValueReader
+
+ for i in 0..()
+ testCase.testAll()
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// func testClose() {
+// let op = AddOperator()
+// // generate tensors
+// var inputTensors = [Tensor]()
+// var outputTensors = [Tensor]()
+// var shape: TensorShape
+//
+// // configure engine
+// let (_, msg) = SerranoEngine.configuredEngine.configureEngine(computationMode: .GPU, serranoCommandQueue: nil, serranoMTLLibrary: nil, systemDefaultGPUDevice: nil)
+//
+// shape = randomShape(dimensions: 2, dimensionSizeRange: [2000, 2000], dataType: .float)
+// for _ in 0..<2 {
+// inputTensors.append(randomTensor(fromShape: shape))
+// print("Generate Input tensor: \(inputTensors.last!.description)")
+// }
+// outputTensors.append(randomTensor(fromShape: shape))
+//
+// op.inputTensors = inputTensors
+// op.outputTensors = outputTensors
+//
+// self.measure {
+// op.compute(.GPU)
+// }
+//
+// SerranoResourceManager.globalManager.releaseAllResources()
+// }
+
+// func testAd() {
+// let (_,_) = SerranoEngine.configuredEngine.configureEngine(computationMode: .GPU)
+// let tensorA = Tensor(randomRampTensor: TensorShape(dataType: .float, shape: [2,4]))
+// let tensorB = Tensor(randomRampTensor: TensorShape(dataType: .float, shape: [2,4]))
+// let tensorC = Tensor(randomRampTensor: TensorShape(dataType: .float, shape: [2,4]))
+// let op = AddOperator(inputTensors: [tensorA, tensorB], outputTensors: [tensorC])
+// op.compute(.GPU)
+//
+// }
+}
+
diff --git a/Tests/SerranoTests/op/basic/binary_op/binary_op_test.swift b/Tests/SerranoTests/op/basic/binary_op/binary_op_test.swift
new file mode 100644
index 0000000..0b591e2
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/binary_op/binary_op_test.swift
@@ -0,0 +1,208 @@
+//
+// binary_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/7/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+import Dispatch
+import Metal
+@testable import Serrano
+
+
+public class OperatorDelegateConvBinaryOp: OperatorDelegateConv {
+
+ public var compareBlock: ([Tensor], Tensor) -> Void
+
+ required public convenience init(compareBlock: (([Tensor], Tensor) -> Void)?) {
+ let blcok = {(inputTensors: [Tensor], resultTensor: Tensor) -> Void in
+ print("NEED OVERRIDE")
+ }
+ self.init(block: blcok)
+ }
+
+ // override this func
+ public init(block: @escaping ([Tensor], Tensor) -> Void) {
+ self.compareBlock = block
+ super.init()
+ }
+
+ override public func compare() {
+ XCTAssertTrue(self.resultTensors.count == 1)
+ XCTAssertTrue(self.resultTensors.first!.count == self.veryfyTensors.first!.count)
+
+ self.compareBlock(self.veryfyTensors, self.resultTensors.first!)
+ }
+}
+
+public class BinaryOpTest: XCTestCase {
+
+ override public func setUp() {
+ super.setUp()
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+
+ }
+
+ override public func tearDown() {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ super.tearDown()
+ }
+
+ // func testExample() {
+ // // This is an example of a functional test case.
+ // // Use XCTAssert and related functions to verify your tests produce the correct results.
+ // }
+ //
+ // func testPerformanceExample() {
+ // // This is an example of a performance test case.
+ // self.measure {
+ // // Put the code you want to measure the time of here.
+ // }
+ // }
+
+ public func testAll() {
+ self.testInit()
+
+ self.testOuputShapesCheck()
+
+ // self.testCPU()
+ //
+ // self.testGPU()
+ //
+ self.testCompute()
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ Test init functions
+ */
+ func testInit() {
+ let numCase = 100
+ let op = BinaryOp()
+ for _ in 0.. [TensorShape]?
+ */
+ func testOuputShapesCheck() {
+ let numCase = 100
+ for i in 0.. [Tensor]
+ func compute(asyncWithInputTensors tensors:[Tensor], computationMode: OperatorComputationMode = SerranoEngine.configuredEngine.defaultComputationMode)
+ */
+
+ func testCompute() {
+ let caseNum = 10
+ let op = BinaryOp()
+
+ // configure engine
+ let (_, _) = SerranoEngine.configuredEngine.configureEngine(computationMode: .GPU, serranoCommandQueue: nil, serranoMTLLibrary: nil, systemDefaultGPUDevice: nil)
+
+ // setup delegate
+ let delegate = OpDelegate(compareBlock: nil)
+ let workingGroup = DispatchGroup()
+ delegate.dispatchGroup = workingGroup
+ op.computationDelegate = delegate
+
+
+ for i in 0.. Void)?) {
+ let blcok = {(inputTensors: [Tensor], resultTensor: Tensor) -> Void in
+ let inputReaderA = inputTensors[0].floatValueReader
+ let inputReaderB = inputTensors[1].floatValueReader
+ let resultReader = resultTensor.floatValueReader
+
+ var val:Float
+ for i in 0..()
+ testCase.testAll()
+ }
+
+// func testRecip() {
+// let tensor = randomTensor(fromShape: TensorShape(dataType: .int, shape: [2, 5]))
+// let tensorb = randomTensor(fromShape: TensorShape(dataType: .int, shape: [2, 5]))
+// let tensorc = randomTensor(fromShape: TensorShape(dataType: .int, shape: [2, 5]))
+// var countB = Int32(tensor.count)
+// print(tensor.flatArrayFloat())
+// print(tensorb.flatArrayFloat())
+//// vvrecf(tensorb.contentsAddress, tensor.contentsAddress, &countB)
+//
+//
+// cblas_ssbmv(CblasRowMajor, CblasLower, countB, 0, 1.0, tensor.contentsAddress, 1, tensorb.contentsAddress, 1, 0.0, tensor.contentsAddress, 1)
+//
+// print(tensor.flatArrayFloat())
+// print(tensorb.flatArrayFloat())
+//// print(tensorc.flatArrayFloat())
+// }
+}
diff --git a/Tests/SerranoTests/op/basic/binary_op/mult_op_test.swift b/Tests/SerranoTests/op/basic/binary_op/mult_op_test.swift
new file mode 100644
index 0000000..60b1ad5
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/binary_op/mult_op_test.swift
@@ -0,0 +1,51 @@
+//
+// mult_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/13/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class MultOpDelegate: OperatorDelegateConvBinaryOp {
+
+ required public convenience init(compareBlock: (([Tensor], Tensor) -> Void)?) {
+ let blcok = {(inputTensors: [Tensor], resultTensor: Tensor) -> Void in
+ let inputReaderA = inputTensors[0].floatValueReader
+ let inputReaderB = inputTensors[1].floatValueReader
+ let resultReader = resultTensor.floatValueReader
+
+ for i in 0..()
+ testCase.testAll()
+ }
+}
+
diff --git a/Tests/SerranoTests/op/basic/binary_op/pow_op_test.swift b/Tests/SerranoTests/op/basic/binary_op/pow_op_test.swift
new file mode 100644
index 0000000..ea4fef0
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/binary_op/pow_op_test.swift
@@ -0,0 +1,63 @@
+//
+// pow_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/13/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+import Accelerate
+@testable import Serrano
+
+class PowOpDelegate: OperatorDelegateConvBinaryOp {
+
+ required public convenience init(compareBlock: (([Tensor], Tensor) -> Void)?) {
+ let blcok = {(inputTensors: [Tensor], resultTensor: Tensor) -> Void in
+ let inputReaderA = inputTensors[0].floatValueReader
+ let inputReaderB = inputTensors[1].floatValueReader
+ let resultReader = resultTensor.floatValueReader
+
+ for i in 0..()
+ testCase.testAll()
+ }
+
+// func testPof() {
+// let a = Tensor(repeatingValue: 0.0, tensorShape: TensorShape(dataType: .float, shape: [2, 3]))
+// let b = Tensor(repeatingValue: 0.0, tensorShape: TensorShape(dataType: .float, shape: [2, 3]))
+// let c = randomTensor(fromShape: TensorShape(dataType: .float, shape: [2, 3]))
+// print(a.flatArrayFloat())
+// print(b.flatArrayFloat())
+// print("==")
+// var count = Int32(c.count)
+// vvpowf(c.contentsAddress, b.contentsAddress, a.contentsAddress, &count)
+// print(a.flatArrayFloat())
+// print(b.flatArrayFloat())
+// print(c.flatArrayFloat())
+//
+// print(powf(0, 0))
+// }
+}
diff --git a/Tests/SerranoTests/op/basic/binary_op/rdiv_op_test.swift b/Tests/SerranoTests/op/basic/binary_op/rdiv_op_test.swift
new file mode 100644
index 0000000..78e36f4
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/binary_op/rdiv_op_test.swift
@@ -0,0 +1,51 @@
+//
+// rdiv_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/13/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class RDivOpDelegate: OperatorDelegateConvBinaryOp {
+
+ required public convenience init(compareBlock: (([Tensor], Tensor) -> Void)?) {
+ let blcok = {(inputTensors: [Tensor], resultTensor: Tensor) -> Void in
+ let inputReaderA = inputTensors[0].floatValueReader
+ let inputReaderB = inputTensors[1].floatValueReader
+ let resultReader = resultTensor.floatValueReader
+
+ for i in 0..()
+ testCase.testAll()
+ }
+}
+
diff --git a/Tests/SerranoTests/op/basic/binary_op/sub_op_test.swift b/Tests/SerranoTests/op/basic/binary_op/sub_op_test.swift
new file mode 100644
index 0000000..3ebc9db
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/binary_op/sub_op_test.swift
@@ -0,0 +1,49 @@
+//
+// sub_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/13/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class SubOpDelegate: OperatorDelegateConvBinaryOp {
+
+ required public convenience init(compareBlock: (([Tensor], Tensor) -> Void)?) {
+ let blcok = {(inputTensors: [Tensor], resultTensor: Tensor) -> Void in
+ let inputReaderA = inputTensors[0].floatValueReader
+ let inputReaderB = inputTensors[1].floatValueReader
+ let resultReader = resultTensor.floatValueReader
+
+ for i in 0..()
+ testCase.testAll()
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/broadcast/broadcast_add_op_test.swift b/Tests/SerranoTests/op/basic/broadcast/broadcast_add_op_test.swift
new file mode 100644
index 0000000..44dd664
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/broadcast/broadcast_add_op_test.swift
@@ -0,0 +1,48 @@
+//
+// broadcast_add_op_test.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/5/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class OperatorDelegateConvBroadcastAddOp: OperatorDelegateConvBroadcastArithmeticOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)? = nil) {
+ let blcok = {(veryfyTensors: [Tensor], resultTensors: [Tensor]) -> Void in
+ let readerA = veryfyTensors[0].floatValueReader
+ let readerB = veryfyTensors[1].floatValueReader
+ let resultReader = resultTensors[0].floatValueReader
+ XCTAssertEqual(veryfyTensors[0].count, resultTensors[0].count)
+ XCTAssertEqual(veryfyTensors[1].count, resultTensors[0].count)
+ for i in 0..()
+ testCase.testAll()
+
+ }
+
+}
diff --git a/Tests/SerranoTests/op/basic/broadcast/broadcast_arithmetic_op_test.swift b/Tests/SerranoTests/op/basic/broadcast/broadcast_arithmetic_op_test.swift
new file mode 100644
index 0000000..2e00450
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/broadcast/broadcast_arithmetic_op_test.swift
@@ -0,0 +1,303 @@
+//
+// broadcast_arithmetic_op_test.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/5/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+import Dispatch
+@testable import Serrano
+
+
+fileprivate func generateShapes(targetShape: TensorShape, valid: Bool) -> [TensorShape] {
+ let targetShapeReversed = Array(targetShape.shapeArray.reversed())
+ var shapesReversed = [[Int]]()
+
+ if valid {
+ //valid
+ for _ in 0..<2 {
+ var newShapeRevsered = Array(targetShapeReversed)
+
+ if randomInt([100, 10000000]) % 3 == 0 { // remove last dim
+ for _ in 0.. Void
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)? = nil) {
+ let blcok = {(veryfyTensors: [Tensor], resultTensors: [Tensor]) -> Void in
+ print("OVERRIDE")
+ }
+ self.init(block: blcok)
+ }
+
+ public init(block: @escaping ([Tensor], [Tensor]) -> Void) {
+ self.compareBlock = block
+ super.init()
+ }
+
+ override public func compare() {
+ // do broadcasting
+ var tensorA = self.veryfyTensors[0]
+ var tensorB = self.veryfyTensors[1]
+ if tensorA.shape != tensorB.shape {
+ let broadcastOp = BroadcastOperator(targetShape: max(tensorA.shape, tensorB.shape))
+ if tensorA.shape < tensorB.shape {
+ // broadcast A
+ broadcastOp.inputTensors = [ self.veryfyTensors[0]]
+ tensorA = SerranoResourceManager.globalManager.allocateTensor(tensorB.shape)
+ broadcastOp.outputTensors = [tensorA]
+ } else if tensorA.shape > tensorB.shape {
+ // broadcast B
+ broadcastOp.inputTensors = [ self.veryfyTensors[1]]
+ tensorB = SerranoResourceManager.globalManager.allocateTensor(tensorA.shape)
+ broadcastOp.outputTensors = [tensorB]
+ }
+ broadcastOp.compute(.CPU)
+ }
+
+ self.compareBlock([tensorA, tensorB], self.resultTensors)
+ }
+}
+
+class BroadcastArithmeticOpTest: XCTestCase {
+
+ override func setUp() {
+ super.setUp()
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ }
+
+ override func tearDown() {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ super.tearDown()
+ }
+
+ func testAll() {
+ self.testInit()
+ self.testOutputShape()
+ self.testInputOutputTensorsCheck()
+ self.testCompute()
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ Target
+ init
+ */
+ func testInit() {
+ let numCase = 100
+ for i in 0.. [TensorShape]?
+ */
+ func testOutputShape() {
+ let numCase = 100
+ let op = Op()
+ for i in 0.. (check: Bool, msg: String)
+ */
+ func testInputOutputTensorsCheck() {
+ let numCase = 100
+ let op = Op()
+ for i in 0..= 8 {
+ shapeA = randomShape(dimensions: 2, dimensionSizeRange: [1000, 1500], dataType: .float)
+ }
+ let shapeB = generateShapes(targetShape: shapeA, valid: true).first!
+ if i % 3 == 0 {
+ inputTensors.append(randomTensor(fromShape: shapeA))
+ inputTensors.append(randomTensor(fromShape: shapeB))
+ } else {
+ inputTensors.append(randomTensor(fromShape: shapeB))
+ inputTensors.append(randomTensor(fromShape: shapeA))
+ }
+ print("Input tensor A: \(inputTensors[0].description)")
+ print("Input tensor B: \(inputTensors[1].description)")
+
+ // output tensor
+ let maxShape = max(inputTensors[0].shape, inputTensors[1].shape)
+ outputTensors.append(randomTensor(fromShape: maxShape))
+ print("Output tensor: \(outputTensors.last!.description)")
+
+ op.inputTensors = inputTensors
+ op.outputTensors = outputTensors
+ delegate.veryfyTensors = inputTensors
+
+ if i % 2 == 0 {
+ print("Run on CPU")
+ workingGroup.enter()
+ op.computeAsync( .CPU)
+ } else {
+ print("Run on GPU")
+ if !SerranoEngine.configuredEngine.hasAvailableGPU() {
+ print("No gpu available, give up Test \(i+1)\n\n\n)")
+ continue
+ }
+ workingGroup.enter()
+ op.computeAsync( .GPU)
+ }
+ workingGroup.wait()
+
+ SerranoResourceManager.globalManager.releaseAllResources()
+ print("Finish Test case \(i+1)\n\n")
+ }
+
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/broadcast/broadcast_div_op_test.swift b/Tests/SerranoTests/op/basic/broadcast/broadcast_div_op_test.swift
new file mode 100644
index 0000000..3e6a20f
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/broadcast/broadcast_div_op_test.swift
@@ -0,0 +1,48 @@
+//
+// broadcast_div_op_test.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class OperatorDelegateConvBroadcastDivOp: OperatorDelegateConvBroadcastArithmeticOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(veryfyTensors: [Tensor], resultTensors: [Tensor]) -> Void in
+ let readerA = veryfyTensors[0].floatValueReader
+ let readerB = veryfyTensors[1].floatValueReader
+ let resultReader = resultTensors[0].floatValueReader
+ XCTAssertEqual(veryfyTensors[0].count, resultTensors[0].count)
+ XCTAssertEqual(veryfyTensors[1].count, resultTensors[0].count)
+ for i in 0..()
+ testCase.testAll()
+ }
+
+}
diff --git a/Tests/SerranoTests/op/basic/broadcast/broadcast_mult_op_test.swift b/Tests/SerranoTests/op/basic/broadcast/broadcast_mult_op_test.swift
new file mode 100644
index 0000000..da733dd
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/broadcast/broadcast_mult_op_test.swift
@@ -0,0 +1,47 @@
+//
+// broadcast_mult_op_test.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class OperatorDelegateConvBroadcastMultOp: OperatorDelegateConvBroadcastArithmeticOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(veryfyTensors: [Tensor], resultTensors: [Tensor]) -> Void in
+ let readerA = veryfyTensors[0].floatValueReader
+ let readerB = veryfyTensors[1].floatValueReader
+ let resultReader = resultTensors[0].floatValueReader
+ XCTAssertEqual(veryfyTensors[0].count, resultTensors[0].count)
+ XCTAssertEqual(veryfyTensors[1].count, resultTensors[0].count)
+ for i in 0..()
+ testCase.testAll()
+ }
+
+}
diff --git a/Tests/SerranoTests/op/basic/broadcast/broadcast_op_test.swift b/Tests/SerranoTests/op/basic/broadcast/broadcast_op_test.swift
new file mode 100644
index 0000000..fba8d16
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/broadcast/broadcast_op_test.swift
@@ -0,0 +1,282 @@
+//
+// broad_cast_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/13/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+fileprivate func verfyShape(targetShape: TensorShape, result: [TensorShape]) -> Bool {
+ for shape in result {
+ if !shape.shapeArray.elementsEqual(targetShape.shapeArray) {
+ return false
+ }
+ }
+ return true
+}
+
+
+fileprivate func generateShapes(targetShape: TensorShape, valid: Bool) -> [TensorShape] {
+ let targetShapeReversed = Array(targetShape.shapeArray.reversed())
+ var shapesReversed = [[Int]]()
+
+ if valid {
+ //valid
+ for _ in 0.. (targetShape: TensorShape, shapes: [TensorShape], valid: Bool) {
+
+ // generate target shape
+ let targetShape = randomShape(dimensions: randomInt([1, 5]), dimensionSizeRange: [1, 5], dataType: .float)
+ let targetShapeReversed = Array(targetShape.shapeArray.reversed())
+
+ var valid = true
+ if randomInt([100, 10000000]) % 3 == 0 { valid = false }
+
+ var shapesReversed = [[Int]]()
+ if valid {
+ //valid
+ for _ in 0.. [TensorShape]?
+ */
+ func testOutputShapes() {
+ let numCase = 100
+
+ for i in 0.. (check: Bool, msg: String)
+ */
+ func testCheck() {
+ let numCase = 100
+
+ for i in 0.. Void)?) {
+ let blcok = {(veryfyTensors: [Tensor], resultTensors: [Tensor]) -> Void in
+ let readerA = veryfyTensors[0].floatValueReader
+ let readerB = veryfyTensors[1].floatValueReader
+ let resultReader = resultTensors[0].floatValueReader
+ XCTAssertEqual(veryfyTensors[0].count, resultTensors[0].count)
+ XCTAssertEqual(veryfyTensors[1].count, resultTensors[0].count)
+ for i in 0..()
+ testCase.testAll()
+ }
+
+}
diff --git a/Tests/SerranoTests/op/basic/broadcast/pad_op_test.swift b/Tests/SerranoTests/op/basic/broadcast/pad_op_test.swift
new file mode 100644
index 0000000..a050bc2
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/broadcast/pad_op_test.swift
@@ -0,0 +1,35 @@
+//
+// pad_op_test.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/18/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+
+class pad_op_test: XCTestCase {
+
+ override func setUp() {
+ super.setUp()
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ }
+
+ override func tearDown() {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ super.tearDown()
+ }
+
+ func testExample() {
+ // This is an example of a functional test case.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+ }
+
+ func testPerformanceExample() {
+ // This is an example of a performance test case.
+ self.measure {
+ // Put the code you want to measure the time of here.
+ }
+ }
+
+}
diff --git a/Tests/SerranoTests/op/basic/broadcast/transpose_op_test.swift b/Tests/SerranoTests/op/basic/broadcast/transpose_op_test.swift
new file mode 100644
index 0000000..9463508
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/broadcast/transpose_op_test.swift
@@ -0,0 +1,285 @@
+//
+// transpose_op.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/23/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+public class OperatorDelegateConvTransposeOperator: OperatorDelegateConv {
+
+ override public func compare() {
+ XCTAssertTrue(self.resultTensors.count == self.veryfyTensors.count)
+
+ for tensorIndex in 0.. [TensorShape]?
+ */
+ func testOutputShape() {
+ let numCase = 100
+ let op = TransposeOperator()
+ for i in 0.. (check: Bool, msg: String)
+ */
+ func testInputOuputTensorsCheck() {
+ let numCase = 100
+ let op = TransposeOperator()
+ for i in 0.. [TensorShape]?
+ */
+ func testOutputShape() {
+ let numCase = 100
+
+ for i in 0.. (check: Bool, msg: String)
+ */
+ func testInputOutputCheck() {
+ let numCase = 100
+ for i in 0..= 18 {
+ dimRange = [1000, 1200]
+ }
+
+ // generate valid
+ var shapeA = randomShape(dimensions: 2, dimensionSizeRange: dimRange, dataType: .int)
+ var shapeB = TensorShape(dataType: .int, shape: [shapeA.shapeArray[1], randomInt(dimRange)])
+
+ // transpose
+ if randomInt([0, 10]) % 2 == 0 {
+ transposeA = true
+ shapeA = shapeA.transposed()
+ }
+ if randomInt([0, 10]) % 2 == 0 {
+ transposeB = true
+ shapeB = shapeB.transposed()
+ }
+ inputTensors.append(randomTensor(fromShape: shapeA))
+ print("Input A: \(shapeA), transpose: \(transposeA)")
+
+ inputTensors.append(randomTensor(fromShape: shapeB))
+ print("Input B: \(shapeB), transpose: \(transposeB)")
+
+ var AShapeArray = shapeA.shapeArray
+ if transposeA {
+ AShapeArray = [AShapeArray[1], AShapeArray[0]]
+ }
+ var BShapeArray = shapeB.shapeArray
+ if transposeB {
+ BShapeArray = [BShapeArray[1], BShapeArray[0]]
+ }
+
+ let outTensor = randomTensor(fromShape: TensorShape(dataType: .int, shape: [AShapeArray[0], BShapeArray[1]]))
+ outputTensors.append(outTensor)
+ print("Output C: \(outTensor.shape)")
+
+ op.transposeA = transposeA
+ op.transposeB = transposeB
+ op.inputTensors = inputTensors
+ op.outputTensors = outputTensors
+ delegate.veryfyTensors = inputTensors
+ delegate.transposeA = transposeA
+ delegate.transposeB = transposeB
+
+ if i % 2 == 0 {
+ print("Run CPU")
+ workGroup.enter()
+ op.computeAsync(.CPU)
+ } else {
+ if !SerranoEngine.configuredEngine.hasAvailableGPU() {
+ print("No available GPU, give up test.\n\n")
+ continue
+ }
+ workGroup.enter()
+ op.computeAsync(.GPU)
+ }
+
+ workGroup.wait()
+
+ SerranoResourceManager.globalManager.releaseAllResources()
+ print("Finish Test \(i+1)\n\n")
+ }
+ }
+
+ func testKernelPerformanceSingle() {
+
+ let op = MatrixMultOperator()
+
+ // gpu initial
+ _ = SerranoEngine.configuredEngine.configureEngine(computationMode: .GPU)
+
+
+
+ var outputTensors = [Tensor]()
+ var inputTensors = [Tensor]()
+
+ // generate tensors
+ let dimRange: [Int] = [1200, 1200]
+
+ // generate valid
+ let shapeA = randomShape(dimensions: 2, dimensionSizeRange: dimRange, dataType: .int)
+ let shapeB = TensorShape(dataType: .int, shape: [shapeA.shapeArray[1], randomInt(dimRange)])
+
+
+ inputTensors.append(randomTensor(fromShape: shapeA))
+ print("Input A: \(shapeA)")
+
+ inputTensors.append(randomTensor(fromShape: shapeB))
+ print("Input B: \(shapeB)")
+
+ let AShapeArray = shapeA.shapeArray
+ let BShapeArray = shapeB.shapeArray
+
+ let outTensor = randomTensor(fromShape: TensorShape(dataType: .int, shape: [AShapeArray[0], BShapeArray[1]]))
+ outputTensors.append(outTensor)
+ print("Output C: \(outTensor.shape)")
+
+ op.inputTensors = inputTensors
+ op.outputTensors = outputTensors
+
+ op.kernel = MatrixMultKernel.Single
+ self.measure {
+ op.compute(.GPU)
+ }
+ }
+
+ func testKernelPerformanceSubMatrix() {
+
+ let op = MatrixMultOperator()
+
+ // gpu initial
+ _ = SerranoEngine.configuredEngine.configureEngine(computationMode: .GPU)
+
+
+
+ var outputTensors = [Tensor]()
+ var inputTensors = [Tensor]()
+
+ // generate tensors
+ let dimRange: [Int] = [1200, 1200]
+
+ // generate valid
+ let shapeA = randomShape(dimensions: 2, dimensionSizeRange: dimRange, dataType: .int)
+ let shapeB = TensorShape(dataType: .int, shape: [shapeA.shapeArray[1], randomInt(dimRange)])
+
+
+ inputTensors.append(randomTensor(fromShape: shapeA))
+ print("Input A: \(shapeA)")
+
+ inputTensors.append(randomTensor(fromShape: shapeB))
+ print("Input B: \(shapeB)")
+
+ let AShapeArray = shapeA.shapeArray
+ let BShapeArray = shapeB.shapeArray
+
+ let outTensor = randomTensor(fromShape: TensorShape(dataType: .int, shape: [AShapeArray[0], BShapeArray[1]]))
+ outputTensors.append(outTensor)
+ print("Output C: \(outTensor.shape)")
+
+ op.inputTensors = inputTensors
+ op.outputTensors = outputTensors
+
+ op.kernel = MatrixMultKernel.SubMatrix
+ self.measure {
+ op.compute(.GPU)
+ }
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/reduce/reduce_max_op_test.swift b/Tests/SerranoTests/op/basic/reduce/reduce_max_op_test.swift
new file mode 100644
index 0000000..0ed3a08
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/reduce/reduce_max_op_test.swift
@@ -0,0 +1,29 @@
+//
+// reduce_max_op_test.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/1/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+
+class ReduceMaxOpDelegate: OperatorDelegateConvReduceOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+
+ }
+ self.init(block: blcok)
+ }
+}
+
+class ReduceMaxOpTest: XCTestCase {
+
+ func test() {
+ let testCase = ReduceOpTest()
+ testCase.testAll()
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/reduce/reduce_mean_op_test.swift b/Tests/SerranoTests/op/basic/reduce/reduce_mean_op_test.swift
new file mode 100644
index 0000000..5b3ea43
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/reduce/reduce_mean_op_test.swift
@@ -0,0 +1,29 @@
+//
+// reduce_mean_op_test.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/2/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+
+class ReduceMeanOpDelegate: OperatorDelegateConvReduceOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+
+ }
+ self.init(block: blcok)
+ }
+}
+
+class ReduceMeanOpTest: XCTestCase {
+
+ func test() {
+ let testCase = ReduceOpTest()
+ testCase.testAll()
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/reduce/reduce_min_op_test.swift b/Tests/SerranoTests/op/basic/reduce/reduce_min_op_test.swift
new file mode 100644
index 0000000..786ef70
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/reduce/reduce_min_op_test.swift
@@ -0,0 +1,29 @@
+//
+// reduce_min_op_test.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 7/1/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+
+class ReduceMinOpDelegate: OperatorDelegateConvReduceOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+
+ }
+ self.init(block: blcok)
+ }
+}
+
+class ReduceMinOpTest: XCTestCase {
+
+ func test() {
+ let testCase = ReduceOpTest()
+ testCase.testAll()
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/reduce/reduce_op_test.swift b/Tests/SerranoTests/op/basic/reduce/reduce_op_test.swift
new file mode 100644
index 0000000..55db232
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/reduce/reduce_op_test.swift
@@ -0,0 +1,289 @@
+//
+// reduce_op_test.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 6/27/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+public class OperatorDelegateConvReduceOp: OperatorDelegateConv {
+
+ public var compareBlock: (Tensor, Tensor) -> Void
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ print()
+ }
+ self.init(block: blcok)
+ }
+
+ // override this func
+ public init(block: @escaping (Tensor, Tensor) -> Void) {
+ self.compareBlock = block
+ super.init()
+ }
+
+ override public func compare() {
+ XCTAssertTrue(self.resultTensors.first!.count == self.veryfyTensors.first!.count)
+
+ for i in 0..: XCTestCase {
+
+ override func setUp() {
+ super.setUp()
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ }
+
+ override func tearDown() {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ super.tearDown()
+ }
+
+
+ func testAll() {
+ print(String(repeating: "=", count: 80) + "\n\n")
+ self.testOutputShape()
+
+ print(String(repeating: "=", count: 80) + "\n\n")
+ self.testInputOutputTensorCheck()
+
+ print(String(repeating: "=", count: 80) + "\n\n")
+ self.testCompute()
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ Test init functions
+ */
+ func testInit() {
+ let numCase = 100
+ let op = ReduceOp(axis: [Int]())
+ for _ in 0.. [TensorShape]?
+ */
+ func testOutputShape() {
+ let numCase = 100
+ let op = ReduceOp(axis: [Int]())
+ for i in 0.. (check: Bool, msg: String)
+ */
+ func testInputOutputTensorCheck() {
+ let numCase = 100
+ let op = ReduceOp(axis: [Int]())
+ for i in 0.. Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+
+ }
+ self.init(block: blcok)
+ }
+}
+
+class ReduceProductOpTest: XCTestCase {
+
+ func test() {
+ let testCase = ReduceOpTest()
+ testCase.testAll()
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/reduce/reduce_sum_op_test.swift b/Tests/SerranoTests/op/basic/reduce/reduce_sum_op_test.swift
new file mode 100644
index 0000000..1f4ec68
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/reduce/reduce_sum_op_test.swift
@@ -0,0 +1,29 @@
+//
+// reduce_sum_op_test.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 6/28/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+
+class ReduceSumOpDelegate: OperatorDelegateConvReduceOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+
+ }
+ self.init(block: blcok)
+ }
+}
+
+class ReduceSumOpTest: XCTestCase {
+
+ func test() {
+ let testCase = ReduceOpTest()
+ testCase.testAll()
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/unary_op/abs_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/abs_op_test.swift
new file mode 100644
index 0000000..1b3b09a
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/abs_op_test.swift
@@ -0,0 +1,48 @@
+//
+// abs_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class AbsOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/unary_op/arccos_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/arccos_op_test.swift
new file mode 100644
index 0000000..c3a1ec4
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/arccos_op_test.swift
@@ -0,0 +1,48 @@
+//
+// arccos_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+import XCTest
+@testable import Serrano
+
+class ArccosOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/unary_op/arccosh_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/arccosh_op_test.swift
new file mode 100644
index 0000000..6575502
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/arccosh_op_test.swift
@@ -0,0 +1,49 @@
+//
+// arccosh_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class ArccoshOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/unary_op/arcsin_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/arcsin_op_test.swift
new file mode 100644
index 0000000..e3dc5e3
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/arcsin_op_test.swift
@@ -0,0 +1,48 @@
+//
+// arcsin_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+import XCTest
+@testable import Serrano
+
+class ArcsinOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/unary_op/arcsinh_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/arcsinh_op_test.swift
new file mode 100644
index 0000000..86e2f7d
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/arcsinh_op_test.swift
@@ -0,0 +1,49 @@
+//
+// arcsinh_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class ArcsinhOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/unary_op/arctan_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/arctan_op_test.swift
new file mode 100644
index 0000000..0e6a051
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/arctan_op_test.swift
@@ -0,0 +1,49 @@
+//
+// arctan_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class ArctanOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/unary_op/arctanh_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/arctanh_op_test.swift
new file mode 100644
index 0000000..458bde7
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/arctanh_op_test.swift
@@ -0,0 +1,50 @@
+//
+// arctanh_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class ArctanhOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+}
+
diff --git a/Tests/SerranoTests/op/basic/unary_op/ceil_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/ceil_op_test.swift
new file mode 100644
index 0000000..5700a4d
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/ceil_op_test.swift
@@ -0,0 +1,49 @@
+//
+// ceil_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class CeilOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+}
+
diff --git a/Tests/SerranoTests/op/basic/unary_op/copy_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/copy_op_test.swift
new file mode 100644
index 0000000..d537d15
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/copy_op_test.swift
@@ -0,0 +1,101 @@
+//
+// copy_op_test.swift
+// serrano
+//
+// Created by ZHONGHAO LIU on 4/16/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+import Dispatch
+@testable import Serrano
+
+
+/**
+ Covenient class for testing
+ */
+class OperatorDelegateConvCopy: OperatorDelegateConv {
+
+ override public func compare() {
+ XCTAssertTrue(self.resultTensors.count == self.veryfyTensors.count)
+
+ for i in 0.. [Tensor]
+ func compute(asyncWithInputTensors tensors: [Tensor], computationMode: OperatorComputationMode = SerranoEngine.configuredEngine.defaultComputationMode)
+ */
+ func testCompute() {
+ let numCase = 100
+ for i in 0.. Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+}
+
diff --git a/Tests/SerranoTests/op/basic/unary_op/cosh_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/cosh_op_test.swift
new file mode 100644
index 0000000..11e239b
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/cosh_op_test.swift
@@ -0,0 +1,49 @@
+//
+// cosh_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class CoshOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/unary_op/degree_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/degree_op_test.swift
new file mode 100644
index 0000000..e138165
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/degree_op_test.swift
@@ -0,0 +1,49 @@
+//
+// degree_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class DegreeOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/unary_op/exp_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/exp_op_test.swift
new file mode 100644
index 0000000..fe6ce8e
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/exp_op_test.swift
@@ -0,0 +1,49 @@
+//
+// exp_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+
+import XCTest
+@testable import Serrano
+
+class ExpOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+
+}
+
diff --git a/Tests/SerranoTests/op/basic/unary_op/expm1_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/expm1_op_test.swift
new file mode 100644
index 0000000..d740120
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/expm1_op_test.swift
@@ -0,0 +1,54 @@
+//
+// expm1_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+
+import XCTest
+@testable import Serrano
+
+class Expm1OpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+
+}
+
+
diff --git a/Tests/SerranoTests/op/basic/unary_op/floor_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/floor_op_test.swift
new file mode 100644
index 0000000..04be3d4
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/floor_op_test.swift
@@ -0,0 +1,48 @@
+//
+// floor_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class FloorOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/unary_op/log10_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/log10_op_test.swift
new file mode 100644
index 0000000..e597f4d
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/log10_op_test.swift
@@ -0,0 +1,52 @@
+//
+// log10_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+
+import XCTest
+@testable import Serrano
+
+class Log10OpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+
+}
diff --git a/Tests/SerranoTests/op/basic/unary_op/log1p_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/log1p_op_test.swift
new file mode 100644
index 0000000..dc0bd5b
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/log1p_op_test.swift
@@ -0,0 +1,53 @@
+//
+// log1p_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+
+import XCTest
+@testable import Serrano
+
+class Log1pOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+
+}
+
diff --git a/Tests/SerranoTests/op/basic/unary_op/log2_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/log2_op_test.swift
new file mode 100644
index 0000000..cca47c8
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/log2_op_test.swift
@@ -0,0 +1,52 @@
+//
+// log2_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+
+import XCTest
+@testable import Serrano
+
+class Log2OpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+
+}
diff --git a/Tests/SerranoTests/op/basic/unary_op/log_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/log_op_test.swift
new file mode 100644
index 0000000..8d263d5
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/log_op_test.swift
@@ -0,0 +1,53 @@
+//
+// log_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+
+import XCTest
+@testable import Serrano
+
+class LogOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+
+}
+
diff --git a/Tests/SerranoTests/op/basic/unary_op/radian_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/radian_op_test.swift
new file mode 100644
index 0000000..284bd46
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/radian_op_test.swift
@@ -0,0 +1,49 @@
+//
+// radian_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class RadianOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/unary_op/rint_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/rint_op_test.swift
new file mode 100644
index 0000000..ccd8e93
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/rint_op_test.swift
@@ -0,0 +1,51 @@
+//
+// rint_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+
+import XCTest
+@testable import Serrano
+
+class RintOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+
+}
diff --git a/Tests/SerranoTests/op/basic/unary_op/round_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/round_op_test.swift
new file mode 100644
index 0000000..d9a037a
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/round_op_test.swift
@@ -0,0 +1,48 @@
+//
+// round_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+
+import XCTest
+@testable import Serrano
+
+class RoundOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+
+}
+
diff --git a/Tests/SerranoTests/op/basic/unary_op/rsqrt_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/rsqrt_op_test.swift
new file mode 100644
index 0000000..08de0a3
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/rsqrt_op_test.swift
@@ -0,0 +1,54 @@
+//
+// rsqrt_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+
+import XCTest
+@testable import Serrano
+
+class RsqrtOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+
+}
diff --git a/Tests/SerranoTests/op/basic/unary_op/sin_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/sin_op_test.swift
new file mode 100644
index 0000000..9b6ccfa
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/sin_op_test.swift
@@ -0,0 +1,50 @@
+//
+// sin_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/5/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class SinOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+}
diff --git a/Tests/SerranoTests/op/basic/unary_op/sinh_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/sinh_op_test.swift
new file mode 100644
index 0000000..2908a3e
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/sinh_op_test.swift
@@ -0,0 +1,50 @@
+//
+// sinh_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class SinhOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+}
+
diff --git a/Tests/SerranoTests/op/basic/unary_op/sqrt_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/sqrt_op_test.swift
new file mode 100644
index 0000000..47632c9
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/sqrt_op_test.swift
@@ -0,0 +1,52 @@
+//
+// sqrt_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+
+import XCTest
+@testable import Serrano
+
+class SqrtOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+
+}
diff --git a/Tests/SerranoTests/op/basic/unary_op/square_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/square_op_test.swift
new file mode 100644
index 0000000..8470cb8
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/square_op_test.swift
@@ -0,0 +1,53 @@
+//
+// square_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+
+import XCTest
+@testable import Serrano
+
+class SquareOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..()
+ testCase.testAll()
+ }
+
+}
+
diff --git a/Tests/SerranoTests/op/basic/unary_op/tan_op_test.swift b/Tests/SerranoTests/op/basic/unary_op/tan_op_test.swift
new file mode 100644
index 0000000..7ce01be
--- /dev/null
+++ b/Tests/SerranoTests/op/basic/unary_op/tan_op_test.swift
@@ -0,0 +1,55 @@
+//
+// tan_op_test.swift
+// SerranoTests
+//
+// Created by ZHONGHAO LIU on 6/6/17.
+// Copyright © 2017 ZHONGHAO LIU. All rights reserved.
+//
+
+import XCTest
+@testable import Serrano
+
+class TanOpDelegate: OperatorDelegateConvUnaryOp {
+
+ required public convenience init(compareBlock: ((Tensor, Tensor) -> Void)?) {
+ let blcok = {(rawTensor: Tensor, resultTensor: Tensor) -> Void in
+ XCTAssertEqual(rawTensor.count, resultTensor.count)
+ let readerReader = rawTensor.floatValueReader
+ let resultReader = resultTensor.floatValueReader
+ for i in 0..