├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── __tests__ ├── arrayLookup.ts ├── bitmaskLookup.ts ├── complexGameState.ts ├── customType.ts ├── enumLookup.ts ├── kitchenSink.ts ├── optionalLookup.ts ├── simpleTypes.ts └── typeLookup.ts ├── jest.config.js ├── lib ├── arrayBufferBuilder.d.ts ├── arrayBufferBuilder.d.ts.map ├── arrayBufferBuilder.js ├── arrayBufferBuilder.js.map ├── index.d.ts ├── index.d.ts.map ├── index.js ├── index.js.map ├── schemaDefiner.d.ts ├── schemaDefiner.d.ts.map ├── schemaDefiner.js ├── schemaDefiner.js.map ├── schemaDefinerTypes.d.ts ├── schemaDefinerTypes.d.ts.map ├── schemaDefinerTypes.js ├── schemaDefinerTypes.js.map ├── utils.d.ts ├── utils.d.ts.map ├── utils.js └── utils.js.map ├── package.json ├── src ├── arrayBufferBuilder.ts ├── index.ts ├── schemaDefiner.ts ├── schemaDefinerTypes.ts └── utils.ts ├── tsconfig.json ├── tsconfig.test.json ├── tslint.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .idea 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth":2, 3 | "singleQuote":true, 4 | "printWidth":120, 5 | "bracketSpacing":false, 6 | "trailingComma": "es5", 7 | "endOfLine": "auto" 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jenny Louthan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Safe Schema 2 | 3 | A dependency-less solution to **safely** serialize your TypeScript models to and from `ArrayBuffer`s. 4 | 5 | ## Features 6 | 7 | - Uses your TypeScript object definitions as a starting point 8 | - Type Safe throughout 9 | - Broad model support 10 | - Lightning Fast due to AOT 11 | - First class support for Type Lookups and Enums 12 | - Supports custom serializations 13 | - Well tested 14 | - No external dependencies 15 | 16 | ## Install 17 | 18 | With npm: 19 | 20 | ``` 21 | $ npm install safe-schema --save 22 | ``` 23 | 24 | With yarn: 25 | 26 | ``` 27 | $ yarn add safe-schema 28 | ``` 29 | 30 | ## Basic Usage 31 | 32 | ```ts 33 | // Import 34 | import {makeSchema, generateSchema} from 'safe-schema'; 35 | 36 | // Define your model 37 | type SimpleMessage = {count: number}; 38 | 39 | // Safely define your schema BASED on your model 40 | const simpleMessageSchema = makeSchema({count: 'uint8'}); 41 | 42 | // ... 43 | 44 | // Initialize the AOT code generator (only once) 45 | const generator = generateSchema(simpleMessageSchema); 46 | 47 | // Turn your object into an ArrayBuffer 48 | const buffer = generator.toBuffer({count: 12}); 49 | 50 | assert(buffer.byteLength === 1); 51 | 52 | // ... 53 | 54 | // Turn your ArrayBuffer back into your object 55 | const result = generator.fromBuffer(buffer); 56 | 57 | // Use your 100% type safe object on the other side of the wire 58 | assert(result.count === 12); 59 | ``` 60 | 61 | ## How It Works 62 | 63 | You define your network schema just as you normally would using TypeScript types, then use `SafeSchema` to generate a runtime version of that schema. You will get full intellisense support when invoking `makeSchema({})`, allowing you to easily define the DataTypes of your model (for instance `number` as `uint16`), as well as the ability to easily **change your schema** and have TypeScript throw the appropriate errors for missing values at compile time. 64 | 65 | Calling `generateSchema(simpleMessageSchema)` generates JavaScript **at runtime** that is hand built to read and write your model to and from an `ArrayBuffer`. There is no switch case behind the scenes, every model generates unique JavaScript which executes **lightning fast**! Only exactly as many bytes will be sent over the wire as needed. 66 | 67 | Take a look at the [complexGameState](__tests__/complexGameState.ts), [kitchenSink](__tests__/kitchenSink.ts), and the other tests for complex and real-world examples. 68 | 69 | ## Why It's Needed 70 | 71 | Every other solution to this problem (protocol buffers, JSON, etc), did not allow me to define my models the way I wanted to, **using TypeScript**. TypeScript's modeling abilities are incredibly feature rich and I did not want to lose any of that functionality just because I needed to serialize my data. That's why I built in first class support for things like Discriminating Unions and Enums, so I can have a richly defined schema with the minimum amount of bytes used. 72 | 73 | ## API Documentation 74 | 75 | - [`numbers`](#numbers) 76 | - [`string`](#string) 77 | - [`boolean`](#boolean) 78 | - [`optional`](#optional) 79 | - [`array`](#array) 80 | - [`type-lookup`](#type-lookup) 81 | - [`enum`](#enum) 82 | - [`number-enum`](#number-enum) 83 | - [`bitmask`](#bitmask) 84 | - [`custom`](#custom) 85 | 86 | ### numbers 87 | 88 | 89 | 90 | When you define your model to be a number in TypeScript you must tell the Schema what type and how big the number is. This is to save on memory over the wire and to not make assumptions. 91 | 92 | Example: 93 | 94 | ```ts 95 | type SimpleMessage = {count: number}; 96 | 97 | const simpleMessageSchema = makeSchema({count: 'int32'}); 98 | ``` 99 | 100 | TypeScript intellisense will only allow values that are valid. The valid values are: 101 | 102 | - `uint8` 103 | - `uint16` 104 | - `uint32` 105 | - `int8` 106 | - `int16` 107 | - `int32` 108 | - `float32` 109 | - `float64` 110 | 111 | ### string 112 | 113 | 114 | 115 | SafeSchema encodes strings into utf16 values, plus one uint16 for its length. It does not currently support strings over 65535 in length. 116 | 117 | Example: 118 | 119 | ```ts 120 | type SimpleMessage = {count: string}; 121 | 122 | const simpleMessageSchema = makeSchema({count: 'string'}); 123 | ``` 124 | 125 | ### boolean 126 | 127 | 128 | 129 | SafeSchema encodes booleans into a single uint8 value 130 | 131 | Example: 132 | 133 | ```ts 134 | type SimpleMessage = {count: boolean}; 135 | 136 | const simpleMessageSchema = makeSchema({count: 'boolean'}); 137 | ``` 138 | 139 | ### optional 140 | 141 | 142 | 143 | SafeSchema allows any value to be optionally defined. This will send an extra byte over the wire to denote if the value is there or not. The type must be defined as optional in your model 144 | 145 | Example: 146 | 147 | ```ts 148 | type SimpleMessage = {count?: number}; 149 | 150 | const simpleMessageSchema = makeSchema({ 151 | count: { 152 | flag: 'optional', 153 | element: 'uint8', 154 | }, 155 | }); 156 | ``` 157 | 158 | ### array 159 | 160 | 161 | 162 | SafeSchema can encode any type as an array. You must specify the max length of the array, either `array-uint8`, `array-uint16`, or `array-uint32` 163 | 164 | Example: 165 | 166 | ```ts 167 | type SimpleMessage = {count: boolean[]}; 168 | const simpleMessageSchema = makeSchema({ 169 | count: { 170 | flag: 'array-uint8', 171 | elements: 'boolean', 172 | }, 173 | }); 174 | ``` 175 | 176 | ```ts 177 | type ComplexMessage = {count: {shoes: number}[]}; 178 | const ComplexMessageSchema = makeSchema({ 179 | count: { 180 | flag: 'array-uint8', 181 | elements: {shoes: 'float64'}, 182 | }, 183 | }); 184 | ``` 185 | 186 | ### type-lookup 187 | 188 | 189 | 190 | SafeSchema has first class support for type lookups. This is useful for Discriminating Unions in TypeScript. 191 | 192 | Example: 193 | 194 | ```ts 195 | type SimpleMessage = {type: 'run'; duration: number} | {type: 'walk'; speed: number}; 196 | 197 | const simpleMessageSchema = makeSchema({ 198 | flag: 'type-lookup', 199 | elements: { 200 | run: {duration: 'uint8'}, 201 | walk: {speed: 'float32'}, 202 | }, 203 | }); 204 | ``` 205 | 206 | ### enum 207 | 208 | 209 | 210 | SafeSchema has first class support TypeScript enums through string unions. It will only send a single byte over the wire. 211 | 212 | Example: 213 | 214 | ```ts 215 | type SimpleMessage = {weapon: 'sword' | 'laser' | 'shoe'}; 216 | 217 | const simpleMessageSchema = makeSchema({ 218 | flag: 'enum', 219 | sword: '0', 220 | laser: '1', 221 | shoe: '2', 222 | }); 223 | ``` 224 | 225 | ### number-enum 226 | 227 | 228 | 229 | SafeSchema has first class support TypeScript enums through number unions. It will only send a single byte over the wire. 230 | 231 | Example: 232 | 233 | ```ts 234 | type SimpleMessage = {team: 1 | 2 | 3}; 235 | 236 | const simpleMessageSchema = makeSchema({ 237 | flag: 'enum', 238 | 1: 1, 239 | 2: 2, 240 | 3: 3, 241 | }); 242 | ``` 243 | 244 | ### bitmask 245 | 246 | 247 | 248 | In rare cases you may want to send a bitmasked value over the wire. You define this as a single object that **only has boolean values**. It will send a single byte over the wire, and be serialized back into the complex object. 249 | 250 | Example: 251 | 252 | ```ts 253 | type BitMaskMessage = { 254 | switcher: { 255 | up: boolean; 256 | down: boolean; 257 | left: boolean; 258 | right: boolean; 259 | }; 260 | }; 261 | 262 | const BitMaskMessageSchema = makeSchema({ 263 | switcher: { 264 | flag: 'bitmask', 265 | up: 0, 266 | down: 1, 267 | left: 2, 268 | right: 3, 269 | }, 270 | }); 271 | ``` 272 | 273 | ### custom 274 | 275 | 276 | 277 | If these data types don't suit all of your needs, you can define your own custom schema type. 278 | 279 | You must define a `customSchemaType` using `makeCustom`. The keys of the object you pass in will be the string you use in your schema. You must define how to read, write, and the size of the model. This `customSchemaType` can now be passed into `makeSchema` so it is aware of your custom keys. 280 | 281 | Note that you must also pass `customSchemaTypes` into the `generate` function 282 | 283 | Example: 284 | 285 | ```ts 286 | import {makeCustomSchema, makeSchema, generateSchema} from 'safe-schema'; 287 | 288 | export const customSchemaTypes = makeCustomSchema({ 289 | specialId: { 290 | // this turns the string 123-456 into two int16's 291 | read: (buffer): string => buffer.readInt16() + '-' + buffer.readInt16(), 292 | write: (model: string, buffer) => { 293 | const specialIdParse = /(-?\d*)-(-?\d*)/; 294 | const specialIdResult = specialIdParse.exec(model); 295 | const x = parseInt(specialIdResult[1]); 296 | const y = parseInt(specialIdResult[2]); 297 | buffer.addInt16(x); 298 | buffer.addInt16(y); 299 | }, 300 | size: (model: string) => 2 + 2, 301 | }, 302 | }); 303 | 304 | type CustomTypeMessage = {testId: string}; 305 | 306 | const CustomTypeMessageSchema = makeSchema({testId: 'specialId'}); 307 | 308 | const generator = generateSchema(CustomTypeMessageSchema, customSchemaTypes); 309 | ``` 310 | -------------------------------------------------------------------------------- /__tests__/arrayLookup.ts: -------------------------------------------------------------------------------- 1 | import {generateSchema, makeSchema} from '../src'; 2 | 3 | type Uint8ArrayMessage = {count: number[]}; 4 | const Uint8ArrayMessageSchema = makeSchema({ 5 | count: { 6 | flag: 'array-uint8', 7 | elements: 'uint32', 8 | }, 9 | }); 10 | 11 | test('array unit8 test', () => { 12 | const generator = generateSchema(Uint8ArrayMessageSchema); 13 | 14 | const buffer = generator.toBuffer({count: [12, 24]}); 15 | expect(buffer.byteLength).toEqual(4 * 2 + 1); 16 | 17 | const result = generator.fromBuffer(buffer); 18 | expect(result.count).toEqual([12, 24]); 19 | }); 20 | 21 | type Uint16ArrayMessage = {count: number[]}; 22 | const Uint16ArrayMessageSchema = makeSchema({ 23 | count: { 24 | flag: 'array-uint16', 25 | elements: 'uint32', 26 | }, 27 | }); 28 | 29 | test('array unit16 test', () => { 30 | const generator = generateSchema(Uint16ArrayMessageSchema); 31 | 32 | const buffer = generator.toBuffer({count: [12, 24]}); 33 | expect(buffer.byteLength).toEqual(4 * 2 + 2); 34 | 35 | const result = generator.fromBuffer(buffer); 36 | expect(result.count).toEqual([12, 24]); 37 | }); 38 | 39 | type Uint32ArrayMessage = {count: number[]}; 40 | const Uint32ArrayMessageSchema = makeSchema({ 41 | count: { 42 | flag: 'array-uint32', 43 | elements: 'uint32', 44 | }, 45 | }); 46 | 47 | test('array unit32 test', () => { 48 | const generator = generateSchema(Uint32ArrayMessageSchema); 49 | 50 | const buffer = generator.toBuffer({count: [12, 24]}); 51 | expect(buffer.byteLength).toEqual(4 * 2 + 4); 52 | 53 | const result = generator.fromBuffer(buffer); 54 | expect(result.count).toEqual([12, 24]); 55 | }); 56 | type Uint8ArrayObjectMessage = {count: {shoes: boolean; count: number}[]}; 57 | const Uint8ArrayObjectMessageSchema = makeSchema({ 58 | count: { 59 | flag: 'array-uint8', 60 | elements: { 61 | count: 'uint8', 62 | shoes: 'boolean', 63 | }, 64 | }, 65 | }); 66 | 67 | test('array unit8 object test', () => { 68 | const generator = generateSchema(Uint8ArrayObjectMessageSchema); 69 | 70 | const buffer = generator.toBuffer({ 71 | count: [ 72 | {shoes: true, count: 12}, 73 | {shoes: false, count: 34}, 74 | ], 75 | }); 76 | expect(buffer.byteLength).toEqual((1 + 1) * 2 + 1); 77 | 78 | const result = generator.fromBuffer(buffer); 79 | expect(result.count).toEqual([ 80 | {shoes: true, count: 12}, 81 | {shoes: false, count: 34}, 82 | ]); 83 | }); 84 | -------------------------------------------------------------------------------- /__tests__/bitmaskLookup.ts: -------------------------------------------------------------------------------- 1 | import {generateSchema, makeSchema} from '../src'; 2 | 3 | type BitMaskMessage = { 4 | switcher: { 5 | up: boolean; 6 | down: boolean; 7 | left: boolean; 8 | right: boolean; 9 | }; 10 | }; 11 | const BitMaskMessageSchema = makeSchema({ 12 | switcher: { 13 | flag: 'bitmask', 14 | up: 0, 15 | down: 1, 16 | left: 2, 17 | right: 3, 18 | }, 19 | }); 20 | 21 | test('bitmask test', () => { 22 | const generator = generateSchema(BitMaskMessageSchema); 23 | 24 | const buffer = generator.toBuffer({switcher: {left: true, down: false, up: true, right: false}}); 25 | 26 | expect(buffer.byteLength).toEqual(1); 27 | 28 | const result = generator.fromBuffer(buffer); 29 | expect(result.switcher).toEqual({left: true, down: false, up: true, right: false}); 30 | }); 31 | -------------------------------------------------------------------------------- /__tests__/complexGameState.ts: -------------------------------------------------------------------------------- 1 | import {generateSchema, makeCustomSchema, makeSchema} from '../src'; 2 | import {ArrayBufferReader} from '../src/arrayBufferBuilder'; 3 | 4 | export type OfFaction = {[faction in PlayableFactionId]?: T}; 5 | export type EntityType = 'infantry' | 'tank' | 'plane' | 'factory'; 6 | export type EntityAction = 'attack' | 'move' | 'spawn-infantry' | 'spawn-tank' | 'spawn-plane' | 'mine'; 7 | 8 | export type PlayableFactionId = 1 | 2 | 3; 9 | 10 | export interface ProcessedVote { 11 | entityId: number; 12 | action: EntityAction; 13 | factionId: PlayableFactionId; 14 | hexId: string; 15 | voteCount?: number; 16 | path?: string[]; 17 | } 18 | 19 | export interface GameState { 20 | gameId: string; 21 | factions: number[]; 22 | factionDetails: OfFaction; 23 | entities: OfFaction; 24 | resources: GameStateResource[]; 25 | generation: number; 26 | roundDuration: number; 27 | roundStart: number; 28 | roundEnd: number; 29 | 30 | totalPlayersVoted: number; 31 | winningVotes: OfFaction; 32 | playersVoted: OfFaction; 33 | scores: OfFaction; 34 | hotEntities: OfFaction<{id: number; count: number}[]>; 35 | notes: OfFaction; 36 | } 37 | export interface VoteNote { 38 | note: string; 39 | action: EntityAction; 40 | fromEntityId: number; 41 | factionId: PlayableFactionId; 42 | toEntityId?: number; 43 | toHexId: string; 44 | fromHexId: string; 45 | voteCount: number; 46 | path: string[]; 47 | } 48 | 49 | export interface FactionDetail { 50 | resourceCount: number; 51 | } 52 | 53 | export interface GameStateEntity { 54 | x: number; 55 | y: number; 56 | id: number; 57 | busy?: GameStateGameEntityBusyDetails; 58 | entityType: EntityType; 59 | health: number; 60 | healthRegenStep: number; 61 | facingDirection: FacingDirection; 62 | } 63 | export enum FacingDirection { 64 | TopLeft = 0, 65 | TopRight = 1, 66 | BottomLeft = 2, 67 | BottomRight = 3, 68 | Left = 4, 69 | Right = 5, 70 | } 71 | 72 | export interface GameStateGameEntityBusyDetails { 73 | ticks: number; 74 | action: EntityAction; 75 | hexId: string; 76 | } 77 | 78 | export interface GameStateResource { 79 | x: number; 80 | y: number; 81 | count: number; 82 | type: ResourceType; 83 | } 84 | export type ResourceType = 'bronze' | 'silver' | 'gold'; 85 | 86 | export const customSchemaTypes = makeCustomSchema({ 87 | hexId: { 88 | read: (buffer): string => buffer.readInt16() + '-' + buffer.readInt16(), 89 | write: (model: string, buffer) => { 90 | const hexIdParse = /(-?\d*)-(-?\d*)/; 91 | const hexIdResult = hexIdParse.exec(model); 92 | const x = parseInt(hexIdResult[1]); 93 | const y = parseInt(hexIdResult[2]); 94 | buffer.addInt16(x); 95 | buffer.addInt16(y); 96 | }, 97 | size: (model: string) => 2 + 2, 98 | }, 99 | byteArray: { 100 | read: (buffer): number[] => { 101 | const byteArray = (len: number, realLength: number, reader: ArrayBufferReader) => { 102 | function padLeft(data, size, paddingChar) { 103 | return (new Array(size + 1).join(paddingChar) + data).slice(-size); 104 | } 105 | let items: number[] = []; 106 | for (let i = 0; i < len; i++) { 107 | items.push( 108 | ...padLeft(reader.readUint32().toString(8), 10, '0') 109 | .split('') 110 | .map((a) => parseInt(a)) 111 | ); 112 | } 113 | return items.slice(0, realLength); 114 | }; 115 | 116 | return byteArray(buffer.readUint32(), buffer.readUint32(), buffer); 117 | }, 118 | write: (model: number[], buffer) => { 119 | const byteLen = Math.ceil(model.length / 10); 120 | buffer.addUint32(byteLen); 121 | buffer.addUint32(model.length); 122 | for (let model_i = 0; model_i < byteLen; model_i++) { 123 | buffer.addUint32(parseInt(model.slice(model_i * 10, (model_i + 1) * 10).join(''), 8)); 124 | } 125 | }, 126 | size: (model: number[]) => 4 + 4 + Math.ceil(model.length / 10) * 4, 127 | }, 128 | }); 129 | 130 | const factionDetailsSchema = makeSchema, typeof customSchemaTypes>({ 131 | '1': { 132 | flag: 'optional', 133 | element: { 134 | resourceCount: 'uint16', 135 | }, 136 | }, 137 | 2: { 138 | flag: 'optional', 139 | element: { 140 | resourceCount: 'uint16', 141 | }, 142 | }, 143 | '3': { 144 | flag: 'optional', 145 | element: { 146 | resourceCount: 'uint16', 147 | }, 148 | }, 149 | }); 150 | 151 | const entitySchema = makeSchema({ 152 | x: 'int16', 153 | y: 'int16', 154 | health: 'uint8', 155 | id: 'uint32', 156 | healthRegenStep: 'uint8', 157 | facingDirection: 'uint8', 158 | entityType: {flag: 'enum', factory: 1, infantry: 2, plane: 3, tank: 4}, 159 | busy: { 160 | flag: 'optional', 161 | element: { 162 | action: { 163 | flag: 'enum', 164 | 'spawn-infantry': 1, 165 | 'spawn-tank': 2, 166 | 'spawn-plane': 3, 167 | attack: 4, 168 | mine: 5, 169 | move: 6, 170 | }, 171 | hexId: 'hexId', 172 | ticks: 'uint32', 173 | }, 174 | }, 175 | }); 176 | 177 | export const GameStateSchema = makeSchema({ 178 | gameId: 'string', 179 | factions: 'byteArray', 180 | factionDetails: factionDetailsSchema, 181 | entities: { 182 | 1: { 183 | flag: 'optional', 184 | element: { 185 | flag: 'array-uint16', 186 | elements: entitySchema, 187 | }, 188 | }, 189 | 2: { 190 | flag: 'optional', 191 | element: { 192 | flag: 'array-uint16', 193 | elements: entitySchema, 194 | }, 195 | }, 196 | 3: { 197 | flag: 'optional', 198 | element: { 199 | flag: 'array-uint16', 200 | elements: entitySchema, 201 | }, 202 | }, 203 | }, 204 | resources: { 205 | flag: 'array-uint16', 206 | elements: { 207 | count: 'uint16', 208 | type: {flag: 'enum', silver: 1, gold: 2, bronze: 3}, 209 | x: 'int16', 210 | y: 'int16', 211 | }, 212 | }, 213 | generation: 'uint16', 214 | roundDuration: 'uint16', 215 | roundStart: 'float64', 216 | roundEnd: 'float64', 217 | 218 | totalPlayersVoted: 'uint16', 219 | winningVotes: { 220 | 1: { 221 | flag: 'optional', 222 | element: { 223 | flag: 'array-uint16', 224 | elements: { 225 | action: { 226 | flag: 'enum', 227 | mine: 1, 228 | attack: 2, 229 | 'spawn-tank': 3, 230 | 'spawn-plane': 4, 231 | 'spawn-infantry': 5, 232 | move: 6, 233 | }, 234 | entityId: 'uint32', 235 | hexId: 'hexId', 236 | factionId: { 237 | flag: 'number-enum', 238 | 1: 1, 239 | 2: 2, 240 | 3: 3, 241 | }, 242 | path: { 243 | flag: 'optional', 244 | element: { 245 | flag: 'array-uint8', 246 | elements: 'string', 247 | }, 248 | }, 249 | voteCount: { 250 | flag: 'optional', 251 | element: 'uint16', 252 | }, 253 | }, 254 | }, 255 | }, 256 | 2: { 257 | flag: 'optional', 258 | element: { 259 | flag: 'array-uint16', 260 | elements: { 261 | action: { 262 | flag: 'enum', 263 | mine: 1, 264 | attack: 2, 265 | 'spawn-tank': 3, 266 | 'spawn-plane': 4, 267 | 'spawn-infantry': 5, 268 | move: 6, 269 | }, 270 | entityId: 'uint32', 271 | hexId: 'hexId', 272 | factionId: { 273 | flag: 'number-enum', 274 | 1: 1, 275 | 2: 2, 276 | 3: 3, 277 | }, 278 | path: { 279 | flag: 'optional', 280 | element: { 281 | flag: 'array-uint8', 282 | elements: 'string', 283 | }, 284 | }, 285 | voteCount: { 286 | flag: 'optional', 287 | element: 'uint16', 288 | }, 289 | }, 290 | }, 291 | }, 292 | 3: { 293 | flag: 'optional', 294 | element: { 295 | flag: 'array-uint16', 296 | elements: { 297 | action: { 298 | flag: 'enum', 299 | mine: 1, 300 | attack: 2, 301 | 'spawn-tank': 3, 302 | 'spawn-plane': 4, 303 | 'spawn-infantry': 5, 304 | move: 6, 305 | }, 306 | entityId: 'uint32', 307 | hexId: 'hexId', 308 | factionId: { 309 | flag: 'number-enum', 310 | 1: 1, 311 | 2: 2, 312 | 3: 3, 313 | }, 314 | path: { 315 | flag: 'optional', 316 | element: { 317 | flag: 'array-uint8', 318 | elements: 'string', 319 | }, 320 | }, 321 | voteCount: { 322 | flag: 'optional', 323 | element: 'uint16', 324 | }, 325 | }, 326 | }, 327 | }, 328 | }, 329 | playersVoted: { 330 | 1: {flag: 'optional', element: 'uint16'}, 331 | 2: {flag: 'optional', element: 'uint16'}, 332 | 3: {flag: 'optional', element: 'uint16'}, 333 | }, 334 | scores: { 335 | 1: {flag: 'optional', element: 'uint32'}, 336 | 2: {flag: 'optional', element: 'uint32'}, 337 | 3: {flag: 'optional', element: 'uint32'}, 338 | }, 339 | hotEntities: { 340 | 1: { 341 | flag: 'optional', 342 | element: { 343 | flag: 'array-uint16', 344 | elements: { 345 | count: 'uint16', 346 | id: 'uint32', 347 | }, 348 | }, 349 | }, 350 | 2: { 351 | flag: 'optional', 352 | element: { 353 | flag: 'array-uint16', 354 | elements: { 355 | count: 'uint16', 356 | id: 'uint32', 357 | }, 358 | }, 359 | }, 360 | 3: { 361 | flag: 'optional', 362 | element: { 363 | flag: 'array-uint16', 364 | elements: { 365 | count: 'uint16', 366 | id: 'uint32', 367 | }, 368 | }, 369 | }, 370 | }, 371 | notes: { 372 | 1: { 373 | flag: 'optional', 374 | element: { 375 | flag: 'array-uint16', 376 | elements: { 377 | action: { 378 | flag: 'enum', 379 | 'spawn-infantry': 1, 380 | 'spawn-plane': 2, 381 | 'spawn-tank': 3, 382 | attack: 4, 383 | mine: 5, 384 | move: 6, 385 | }, 386 | factionId: { 387 | flag: 'number-enum', 388 | 1: 1, 389 | 2: 2, 390 | 3: 3, 391 | }, 392 | fromEntityId: 'int16', 393 | fromHexId: 'hexId', 394 | note: 'string', 395 | path: { 396 | flag: 'array-uint8', 397 | elements: 'string', 398 | }, 399 | toEntityId: { 400 | flag: 'optional', 401 | element: 'uint32', 402 | }, 403 | toHexId: 'hexId', 404 | voteCount: 'uint16', 405 | }, 406 | }, 407 | }, 408 | 2: { 409 | flag: 'optional', 410 | element: { 411 | flag: 'array-uint16', 412 | elements: { 413 | action: { 414 | flag: 'enum', 415 | 'spawn-infantry': 1, 416 | 'spawn-plane': 2, 417 | 'spawn-tank': 3, 418 | attack: 4, 419 | mine: 5, 420 | move: 6, 421 | }, 422 | factionId: { 423 | flag: 'number-enum', 424 | 1: 1, 425 | 2: 2, 426 | 3: 3, 427 | }, 428 | fromEntityId: 'int16', 429 | fromHexId: 'hexId', 430 | note: 'string', 431 | path: { 432 | flag: 'array-uint8', 433 | elements: 'string', 434 | }, 435 | toEntityId: { 436 | flag: 'optional', 437 | element: 'uint32', 438 | }, 439 | toHexId: 'hexId', 440 | voteCount: 'uint16', 441 | }, 442 | }, 443 | }, 444 | 3: { 445 | flag: 'optional', 446 | element: { 447 | flag: 'array-uint16', 448 | elements: { 449 | action: { 450 | flag: 'enum', 451 | 'spawn-infantry': 1, 452 | 'spawn-plane': 2, 453 | 'spawn-tank': 3, 454 | attack: 4, 455 | mine: 5, 456 | move: 6, 457 | }, 458 | factionId: { 459 | flag: 'number-enum', 460 | 1: 1, 461 | 2: 2, 462 | 3: 3, 463 | }, 464 | fromEntityId: 'int16', 465 | fromHexId: 'hexId', 466 | note: 'string', 467 | path: { 468 | flag: 'array-uint8', 469 | elements: 'string', 470 | }, 471 | toEntityId: { 472 | flag: 'optional', 473 | element: 'uint32', 474 | }, 475 | toHexId: 'hexId', 476 | voteCount: 'uint16', 477 | }, 478 | }, 479 | }, 480 | }, 481 | }); 482 | 483 | test('complex game state test', () => { 484 | const generator = generateSchema(GameStateSchema, customSchemaTypes); 485 | 486 | const model: GameState = { 487 | gameId: 'abc', 488 | roundDuration: 200, 489 | roundEnd: 100, 490 | roundStart: 200, 491 | 492 | resources: [ 493 | { 494 | x: 1, 495 | y: 2, 496 | type: 'bronze', 497 | count: 3, 498 | }, 499 | { 500 | x: 11, 501 | y: 12, 502 | type: 'gold', 503 | count: 13, 504 | }, 505 | { 506 | x: 21, 507 | y: 22, 508 | type: 'silver', 509 | count: 23, 510 | }, 511 | ], 512 | factionDetails: {1: {resourceCount: 12}, 2: {resourceCount: 13}, 3: {resourceCount: 14}}, 513 | entities: { 514 | 1: [ 515 | { 516 | busy: undefined, 517 | x: 1, 518 | y: 2, 519 | entityType: 'factory', 520 | facingDirection: FacingDirection.BottomLeft, 521 | health: 3, 522 | healthRegenStep: 4, 523 | id: 5, 524 | }, 525 | ], 526 | 2: [ 527 | { 528 | busy: undefined, 529 | x: 3, 530 | y: 4, 531 | entityType: 'factory', 532 | facingDirection: FacingDirection.Right, 533 | health: 3, 534 | healthRegenStep: 4, 535 | id: 6, 536 | }, 537 | ], 538 | 3: [ 539 | { 540 | busy: undefined, 541 | x: 31, 542 | y: 32, 543 | entityType: 'factory', 544 | facingDirection: FacingDirection.BottomLeft, 545 | health: 33, 546 | healthRegenStep: 34, 547 | id: 35, 548 | }, 549 | { 550 | x: 41, 551 | y: 42, 552 | busy: { 553 | action: 'attack', 554 | hexId: '123-456', 555 | ticks: 20000000, 556 | }, 557 | entityType: 'tank', 558 | facingDirection: FacingDirection.TopRight, 559 | health: 43, 560 | healthRegenStep: 44, 561 | id: 45, 562 | }, 563 | ], 564 | }, 565 | factions: [ 566 | 0, 567 | 1, 568 | 2, 569 | 3, 570 | 4, 571 | 5, 572 | 6, 573 | 7, 574 | 0, 575 | 1, 576 | 2, 577 | 3, 578 | 4, 579 | 5, 580 | 6, 581 | 7, 582 | 0, 583 | 1, 584 | 2, 585 | 3, 586 | 4, 587 | 5, 588 | 6, 589 | 7, 590 | 0, 591 | 1, 592 | 2, 593 | 3, 594 | 4, 595 | 5, 596 | 6, 597 | 7, 598 | 0, 599 | 1, 600 | 2, 601 | 3, 602 | 4, 603 | 5, 604 | 6, 605 | 7, 606 | ], 607 | totalPlayersVoted: 24, 608 | generation: 5000, 609 | 610 | hotEntities: { 611 | 1: [{count: 1, id: 2}], 612 | 2: [{count: 3, id: 4}], 613 | 3: [ 614 | {count: 5, id: 6}, 615 | {count: 5, id: 6}, 616 | {count: 5, id: 6}, 617 | {count: 5, id: 6}, 618 | {count: 7, id: 8}, 619 | ], 620 | }, 621 | winningVotes: { 622 | 1: [ 623 | { 624 | action: 'mine', 625 | factionId: 1, 626 | path: ['a', 'b'], 627 | voteCount: 12, 628 | entityId: 333, 629 | hexId: '12-32331', 630 | }, 631 | ], 632 | 2: [ 633 | { 634 | action: 'spawn-infantry', 635 | factionId: 1, 636 | path: ['a', 'b'], 637 | voteCount: 12, 638 | entityId: 333, 639 | hexId: '12-32331', 640 | }, 641 | ], 642 | 3: [ 643 | { 644 | action: 'spawn-plane', 645 | factionId: 1, 646 | path: ['a', 'b'], 647 | voteCount: 12, 648 | entityId: 3233, 649 | hexId: '112-32331', 650 | }, 651 | ], 652 | }, 653 | playersVoted: { 654 | 1: 0, 655 | 2: 1, 656 | 3: 2, 657 | }, 658 | scores: { 659 | 1: 3, 660 | 2: 4, 661 | 3: 5, 662 | }, 663 | notes: { 664 | 1: [ 665 | { 666 | action: 'mine', 667 | factionId: 1, 668 | fromEntityId: 123, 669 | fromHexId: '423--334', 670 | note: 'hi hi ho', 671 | path: ['a', 'b'], 672 | toEntityId: 789, 673 | toHexId: '888-777', 674 | voteCount: 12, 675 | }, 676 | ], 677 | 2: [ 678 | { 679 | toEntityId: undefined, 680 | action: 'attack', 681 | factionId: 1, 682 | fromEntityId: 123, 683 | fromHexId: '423--334', 684 | note: 'hi hi ho', 685 | path: ['a', 'b'], 686 | toHexId: '888-777', 687 | voteCount: 12, 688 | }, 689 | ], 690 | 3: [ 691 | { 692 | action: 'spawn-tank', 693 | factionId: 1, 694 | fromEntityId: 123, 695 | fromHexId: '423--334', 696 | note: 'hi hi ho', 697 | path: ['a', 'b'], 698 | toEntityId: 789, 699 | toHexId: '444-777', 700 | voteCount: 12, 701 | }, 702 | ], 703 | }, 704 | }; 705 | const buffer = generator.toBuffer(model); 706 | expect(buffer.byteLength).toEqual(452); 707 | 708 | const result = generator.fromBuffer(buffer); 709 | expect(result).toEqual(model); 710 | }); 711 | -------------------------------------------------------------------------------- /__tests__/customType.ts: -------------------------------------------------------------------------------- 1 | import {generateSchema, makeCustomSchema, makeSchema} from '../src'; 2 | 3 | type CustomTypeMessage = {testId: string}; 4 | 5 | export const customSchemaTypes = makeCustomSchema({ 6 | specialId: { 7 | read: (buffer): string => buffer.readInt16() + '-' + buffer.readInt16(), 8 | write: (model: string, buffer) => { 9 | const specialIdParse = /(-?\d*)-(-?\d*)/; 10 | const specialIdResult = specialIdParse.exec(model); 11 | const x = parseInt(specialIdResult[1]); 12 | const y = parseInt(specialIdResult[2]); 13 | buffer.addInt16(x); 14 | buffer.addInt16(y); 15 | }, 16 | size: (model: string) => 2 + 2, 17 | }, 18 | }); 19 | 20 | const CustomTypeMessageSchema = makeSchema({testId: 'specialId'}); 21 | 22 | test('custom type test', () => { 23 | const generator = generateSchema(CustomTypeMessageSchema, customSchemaTypes); 24 | 25 | const buffer = generator.toBuffer({testId: '12345-12344'}); 26 | expect(buffer.byteLength).toEqual(4); 27 | 28 | const result = generator.fromBuffer(buffer); 29 | expect(result.testId).toEqual('12345-12344'); 30 | }); 31 | -------------------------------------------------------------------------------- /__tests__/enumLookup.ts: -------------------------------------------------------------------------------- 1 | import {generateSchema, makeSchema} from '../src'; 2 | 3 | type EnumMessage = {switcher: 'a' | 'b' | 'c'}; 4 | const EnumMessageSchema = makeSchema({ 5 | switcher: { 6 | flag: 'enum', 7 | a: 1, 8 | b: 2, 9 | c: 3, 10 | }, 11 | }); 12 | 13 | test('enum test', () => { 14 | const generator = generateSchema(EnumMessageSchema); 15 | 16 | const buffer = generator.toBuffer({switcher: 'a'}); 17 | expect(buffer.byteLength).toEqual(1); 18 | 19 | const result = generator.fromBuffer(buffer); 20 | expect(result.switcher).toEqual('a'); 21 | }); 22 | -------------------------------------------------------------------------------- /__tests__/kitchenSink.ts: -------------------------------------------------------------------------------- 1 | import {generateSchema, makeSchema} from '../src'; 2 | 3 | type Weapons = 'sword' | 'laser'; 4 | 5 | type KitchenSinkMessage = { 6 | message: string; 7 | items: ( 8 | | { 9 | type: 'weapon'; 10 | strength: number; 11 | weapon: Weapons; 12 | } 13 | | { 14 | type: 'run'; 15 | duration: number; 16 | direction: { 17 | up: boolean; 18 | down: boolean; 19 | left: boolean; 20 | right: boolean; 21 | }; 22 | } 23 | )[]; 24 | }; 25 | 26 | const KitchenSinkMessageSchema = makeSchema({ 27 | message: 'string', 28 | items: { 29 | flag: 'array-uint8', 30 | elements: { 31 | flag: 'type-lookup', 32 | elements: { 33 | run: { 34 | duration: 'uint16', 35 | direction: { 36 | flag: 'bitmask', 37 | up: 0, 38 | down: 1, 39 | left: 2, 40 | right: 3, 41 | }, 42 | }, 43 | weapon: { 44 | weapon: { 45 | flag: 'enum', 46 | laser: 0, 47 | sword: 1, 48 | }, 49 | strength: 'float32', 50 | }, 51 | }, 52 | }, 53 | }, 54 | }); 55 | 56 | test('kitchen sink test', () => { 57 | const generator = generateSchema(KitchenSinkMessageSchema); 58 | 59 | const buffer = generator.toBuffer({ 60 | message: 'The game is on!', 61 | items: [ 62 | {type: 'weapon', strength: 12, weapon: 'sword'}, 63 | {type: 'weapon', strength: 34, weapon: 'laser'}, 64 | {type: 'run', duration: 45, direction: {down: true, left: false, right: true, up: false}}, 65 | ], 66 | }); 67 | expect(buffer.byteLength).toEqual(49); 68 | 69 | const result = generator.fromBuffer(buffer); 70 | expect(result).toEqual({ 71 | message: 'The game is on!', 72 | items: [ 73 | {type: 'weapon', strength: 12, weapon: 'sword'}, 74 | {type: 'weapon', strength: 34, weapon: 'laser'}, 75 | {type: 'run', duration: 45, direction: {down: true, left: false, right: true, up: false}}, 76 | ], 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /__tests__/optionalLookup.ts: -------------------------------------------------------------------------------- 1 | import {generateSchema, makeSchema} from '../src'; 2 | 3 | type UInt8Message = {count?: number}; 4 | const UInt8MessageSchema = makeSchema({ 5 | count: { 6 | flag: 'optional', 7 | element: 'uint8', 8 | }, 9 | }); 10 | 11 | test('uint8 optional test set', () => { 12 | const generator = generateSchema(UInt8MessageSchema); 13 | 14 | const buffer = generator.toBuffer({count: 12}); 15 | expect(buffer.byteLength).toEqual(2); 16 | 17 | const result = generator.fromBuffer(buffer); 18 | expect(result.count).toEqual(12); 19 | }); 20 | 21 | test('uint8 optional test unset', () => { 22 | const generator = generateSchema(UInt8MessageSchema); 23 | 24 | const buffer = generator.toBuffer({count: undefined}); 25 | expect(buffer.byteLength).toEqual(1); 26 | 27 | const result = generator.fromBuffer(buffer); 28 | expect(result.count).toEqual(undefined); 29 | }); 30 | -------------------------------------------------------------------------------- /__tests__/simpleTypes.ts: -------------------------------------------------------------------------------- 1 | import {generateSchema, makeSchema} from '../src'; 2 | 3 | type UInt8Message = {count: number}; 4 | const UInt8MessageSchema = makeSchema({count: 'uint8'}); 5 | 6 | test('uint8 test', () => { 7 | const generator = generateSchema(UInt8MessageSchema); 8 | 9 | const buffer = generator.toBuffer({count: 12}); 10 | expect(buffer.byteLength).toEqual(1); 11 | 12 | const result = generator.fromBuffer(buffer); 13 | expect(result.count).toEqual(12); 14 | }); 15 | 16 | type UInt16Message = {count: number}; 17 | const UInt16MessageSchema = makeSchema({count: 'uint16'}); 18 | 19 | test('uint16 test', () => { 20 | const generator = generateSchema(UInt16MessageSchema); 21 | 22 | const buffer = generator.toBuffer({count: 65530}); 23 | expect(buffer.byteLength).toEqual(2); 24 | 25 | const result = generator.fromBuffer(buffer); 26 | expect(result.count).toEqual(65530); 27 | }); 28 | 29 | type UInt32Message = {count: number}; 30 | const UInt32MessageSchema = makeSchema({count: 'uint32'}); 31 | 32 | test('uint32 test', () => { 33 | const generator = generateSchema(UInt32MessageSchema); 34 | 35 | const buffer = generator.toBuffer({count: 123123123}); 36 | expect(buffer.byteLength).toEqual(4); 37 | 38 | const result = generator.fromBuffer(buffer); 39 | expect(result.count).toEqual(123123123); 40 | }); 41 | 42 | type Int8Message = {count: number}; 43 | const Int8MessageSchema = makeSchema({count: 'int8'}); 44 | 45 | test('int8 test', () => { 46 | const generator = generateSchema(Int8MessageSchema); 47 | 48 | const buffer = generator.toBuffer({count: 12}); 49 | expect(buffer.byteLength).toEqual(1); 50 | 51 | const result = generator.fromBuffer(buffer); 52 | expect(result.count).toEqual(12); 53 | }); 54 | 55 | type Int16Message = {count: number}; 56 | const Int16MessageSchema = makeSchema({count: 'int16'}); 57 | 58 | test('int16 test', () => { 59 | const generator = generateSchema(Int16MessageSchema); 60 | 61 | const buffer = generator.toBuffer({count: 25530}); 62 | expect(buffer.byteLength).toEqual(2); 63 | 64 | const result = generator.fromBuffer(buffer); 65 | expect(result.count).toEqual(25530); 66 | }); 67 | 68 | type Int32Message = {count: number}; 69 | const Int32MessageSchema = makeSchema({count: 'int32'}); 70 | 71 | test('int32 test', () => { 72 | const generator = generateSchema(Int32MessageSchema); 73 | 74 | const buffer = generator.toBuffer({count: 123123123}); 75 | expect(buffer.byteLength).toEqual(4); 76 | 77 | const result = generator.fromBuffer(buffer); 78 | expect(result.count).toEqual(123123123); 79 | }); 80 | 81 | type Float32Message = {count: number}; 82 | const Float32MessageSchema = makeSchema({count: 'float32'}); 83 | 84 | test('float32 test', () => { 85 | const generator = generateSchema(Float32MessageSchema); 86 | 87 | const buffer = generator.toBuffer({count: 12.34}); 88 | expect(buffer.byteLength).toEqual(4); 89 | 90 | const result = generator.fromBuffer(buffer); 91 | expect(result.count).toBeCloseTo(12.34); 92 | }); 93 | 94 | type Float64Message = {count: number}; 95 | const Float64MessageSchema = makeSchema({count: 'float64'}); 96 | 97 | test('float64 test', () => { 98 | const generator = generateSchema(Float64MessageSchema); 99 | 100 | const buffer = generator.toBuffer({count: 45.67}); 101 | expect(buffer.byteLength).toEqual(8); 102 | 103 | const result = generator.fromBuffer(buffer); 104 | expect(result.count).toBeCloseTo(45.67); 105 | }); 106 | 107 | type BooleanMessage = {count: boolean}; 108 | const BooleanMessageSchema = makeSchema({count: 'boolean'}); 109 | 110 | test('boolean test', () => { 111 | const generator = generateSchema(BooleanMessageSchema); 112 | 113 | const buffer = generator.toBuffer({count: true}); 114 | expect(buffer.byteLength).toEqual(1); 115 | 116 | const result = generator.fromBuffer(buffer); 117 | expect(result.count).toEqual(true); 118 | }); 119 | 120 | type StringMessage = {count: string}; 121 | const StringMessageSchema = makeSchema({count: 'string'}); 122 | 123 | test('string test', () => { 124 | const generator = generateSchema(StringMessageSchema); 125 | 126 | const buffer = generator.toBuffer({count: 'Hi, how are you?'}); 127 | expect(buffer.byteLength).toEqual('Hi, how are you?'.length * 2 + 2); 128 | 129 | const result = generator.fromBuffer(buffer); 130 | expect(result.count).toEqual('Hi, how are you?'); 131 | }); 132 | -------------------------------------------------------------------------------- /__tests__/typeLookup.ts: -------------------------------------------------------------------------------- 1 | import {generateSchema, makeSchema} from '../src'; 2 | import {assertType} from '../src/utils'; 3 | 4 | type Messages = 5 | | { 6 | type: 'ping'; 7 | value: number; 8 | } 9 | | { 10 | type: 'pong'; 11 | shoes: number; 12 | }; 13 | 14 | const MessageSchema = makeSchema({ 15 | flag: 'type-lookup', 16 | elements: { 17 | ping: {value: 'uint8'}, 18 | pong: {shoes: 'float32'}, 19 | }, 20 | }); 21 | 22 | test('type lookup test', () => { 23 | const generator = generateSchema(MessageSchema); 24 | 25 | const buffer = generator.toBuffer({type: 'pong', shoes: 12}); 26 | 27 | expect(buffer.byteLength).toEqual(5); 28 | 29 | const result = generator.fromBuffer(buffer); 30 | expect(result.type).toEqual('pong'); 31 | assertType<'pong'>(result.type); 32 | expect(result.shoes).toEqual(12); 33 | }); 34 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | globals: { 4 | 'ts-jest': { 5 | tsConfig: './tsconfig.test.json', 6 | }, 7 | }, 8 | testEnvironment: 'node', 9 | collectCoverage: false, 10 | coveragePathIgnorePatterns: ['/node_modules/'], 11 | testPathIgnorePatterns: ['/node_modules/', '/lib/'], 12 | }; 13 | -------------------------------------------------------------------------------- /lib/arrayBufferBuilder.d.ts: -------------------------------------------------------------------------------- 1 | export declare class ArrayBufferBuilder { 2 | private initialBufferSize; 3 | private sizeIsExact; 4 | buffer: ArrayBuffer; 5 | uint: Uint8Array; 6 | curPosition: number; 7 | view: DataView; 8 | static caches: Map; 13 | static biggestCachableBufferSize: number; 14 | constructor(initialBufferSize?: number, sizeIsExact?: boolean); 15 | addBits(...bools: boolean[]): void; 16 | addBoolean(value: boolean): void; 17 | addFloat32(value: number): void; 18 | addFloat64(value: number): void; 19 | addInt16(value: number): void; 20 | addInt32(value: number): void; 21 | addInt32Optional(value?: number): void; 22 | addInt8(value: number): void; 23 | addInt8Optional(value?: number): void; 24 | addLoop(items: T[], callback: (t: T) => void): void; 25 | addString(str: string): void; 26 | addArrayBuffer(buff: ArrayBuffer): void; 27 | addSwitch(n: TType, options: { 28 | [key in TType]: TResult; 29 | }): void; 30 | addUint16(value: number): void; 31 | addUint32(value: number): void; 32 | addUint8(value: number): void; 33 | buildBuffer(): ArrayBuffer; 34 | testSize(added: number): void; 35 | dispose(): void; 36 | } 37 | export declare class ArrayBufferReader { 38 | private dv; 39 | private index; 40 | constructor(buffer: ArrayBuffer | ArrayBufferLike); 41 | done(): void; 42 | loop(callback: () => T, size?: '16' | '8'): T[]; 43 | readBits(): boolean[]; 44 | readBoolean(): boolean; 45 | readFloat32(): number; 46 | readFloat64(): number; 47 | readInt16(): number; 48 | readInt32(): number; 49 | readInt32Optional(): number | undefined; 50 | readInt8(): number; 51 | readInt8Optional(): number | undefined; 52 | readArrayBuffer(): ArrayBuffer; 53 | readString(): string; 54 | readUint16(): number; 55 | readUint32(): number; 56 | readUint8(): number; 57 | switch(callback: { 58 | [key in TOptions]: () => TResult; 59 | }): TResult; 60 | } 61 | //# sourceMappingURL=arrayBufferBuilder.d.ts.map -------------------------------------------------------------------------------- /lib/arrayBufferBuilder.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"arrayBufferBuilder.d.ts","sourceRoot":"","sources":["../src/arrayBufferBuilder.ts"],"names":[],"mappings":"AAEA,qBAAa,kBAAkB;IASjB,OAAO,CAAC,iBAAiB;IAAe,OAAO,CAAC,WAAW;IARvE,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,SAAK;IAChB,IAAI,EAAE,QAAQ,CAAC;IAEf,MAAM,CAAC,MAAM;gBAA4B,WAAW;cAAQ,QAAQ;cAAQ,UAAU;OAAK;IAC3F,MAAM,CAAC,yBAAyB,SAAK;gBAEjB,iBAAiB,GAAE,MAAW,EAAU,WAAW,GAAE,OAAe;IAiBxF,OAAO,CAAC,GAAG,KAAK,EAAE,OAAO,EAAE;IAG3B,UAAU,CAAC,KAAK,EAAE,OAAO;IAMzB,UAAU,CAAC,KAAK,EAAE,MAAM;IAMxB,UAAU,CAAC,KAAK,EAAE,MAAM;IAMxB,QAAQ,CAAC,KAAK,EAAE,MAAM;IAMtB,QAAQ,CAAC,KAAK,EAAE,MAAM;IAMtB,gBAAgB,CAAC,KAAK,CAAC,EAAE,MAAM;IAQ/B,OAAO,CAAC,KAAK,EAAE,MAAM;IAMrB,eAAe,CAAC,KAAK,CAAC,EAAE,MAAM;IAO9B,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI;IAO/C,SAAS,CAAC,GAAG,EAAE,MAAM;IAQrB,cAAc,CAAC,IAAI,EAAE,WAAW;IAOhC,SAAS,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,EAAE,OAAO,SAAS,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE;SAAE,GAAG,IAAI,KAAK,GAAG,OAAO;KAAC;IAI7G,SAAS,CAAC,KAAK,EAAE,MAAM;IAMvB,SAAS,CAAC,KAAK,EAAE,MAAM;IAMvB,QAAQ,CAAC,KAAK,EAAE,MAAM;IAMtB,WAAW,IAAI,WAAW;IAY1B,QAAQ,CAAC,KAAK,EAAE,MAAM;IAOtB,OAAO;CAmBR;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,EAAE,CAAW;IACrB,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE,WAAW,GAAG,eAAe;IAKjD,IAAI;IAMJ,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,IAAI,GAAE,IAAI,GAAG,GAAU,GAAG,CAAC,EAAE;IAiBxD,QAAQ,IAAI,OAAO,EAAE;IASrB,WAAW;IAIX,WAAW,IAAI,MAAM;IAMrB,WAAW,IAAI,MAAM;IAMrB,SAAS,IAAI,MAAM;IAMnB,SAAS,IAAI,MAAM;IAMnB,iBAAiB,IAAI,MAAM,GAAG,SAAS;IASvC,QAAQ,IAAI,MAAM;IAKlB,gBAAgB,IAAI,MAAM,GAAG,SAAS;IAStC,eAAe;IAOf,UAAU;IASV,UAAU,IAAI,MAAM;IAMpB,UAAU,IAAI,MAAM;IAMpB,SAAS,IAAI,MAAM;IAMnB,MAAM,CAAC,QAAQ,SAAS,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE;SAAE,GAAG,IAAI,QAAQ,GAAG,MAAM,OAAO;KAAC,GAAG,OAAO;CAOhG"} -------------------------------------------------------------------------------- /lib/arrayBufferBuilder.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.ArrayBufferReader = exports.ArrayBufferBuilder = void 0; 4 | const utils_1 = require("./utils"); 5 | class ArrayBufferBuilder { 6 | constructor(initialBufferSize = 50, sizeIsExact = false) { 7 | this.initialBufferSize = initialBufferSize; 8 | this.sizeIsExact = sizeIsExact; 9 | this.curPosition = 0; 10 | if (sizeIsExact && initialBufferSize <= ArrayBufferBuilder.biggestCachableBufferSize) { 11 | if (ArrayBufferBuilder.caches.has(initialBufferSize)) { 12 | const cache = ArrayBufferBuilder.caches.get(initialBufferSize); 13 | this.buffer = cache.buffer; 14 | this.view = cache.view; 15 | this.uint = cache.uint; 16 | ArrayBufferBuilder.caches.delete(initialBufferSize); 17 | return; 18 | } 19 | } 20 | this.buffer = new ArrayBuffer(initialBufferSize); 21 | this.uint = new Uint8Array(this.buffer); 22 | this.view = new DataView(this.buffer); 23 | } 24 | addBits(...bools) { 25 | this.addUint8(parseInt('1' + bools.map((a) => (a ? '1' : '0')).join(''), 2)); 26 | } 27 | addBoolean(value) { 28 | !this.sizeIsExact && this.testSize(1); 29 | this.view.setUint8(this.curPosition, value ? 1 : 0); 30 | this.curPosition += 1; 31 | } 32 | addFloat32(value) { 33 | !this.sizeIsExact && this.testSize(4); 34 | this.view.setFloat32(this.curPosition, value); 35 | this.curPosition += 4; 36 | } 37 | addFloat64(value) { 38 | !this.sizeIsExact && this.testSize(8); 39 | this.view.setFloat64(this.curPosition, value); 40 | this.curPosition += 8; 41 | } 42 | addInt16(value) { 43 | !this.sizeIsExact && this.testSize(2); 44 | this.view.setInt16(this.curPosition, value); 45 | this.curPosition += 2; 46 | } 47 | addInt32(value) { 48 | !this.sizeIsExact && this.testSize(4); 49 | this.view.setInt32(this.curPosition, value); 50 | this.curPosition += 4; 51 | } 52 | addInt32Optional(value) { 53 | if (value === undefined) { 54 | this.addInt32(-1); 55 | } 56 | else { 57 | this.addInt32(value); 58 | } 59 | } 60 | addInt8(value) { 61 | !this.sizeIsExact && this.testSize(1); 62 | this.view.setInt8(this.curPosition, value); 63 | this.curPosition += 1; 64 | } 65 | addInt8Optional(value) { 66 | if (value === undefined) { 67 | this.addInt8(-1); 68 | } 69 | else { 70 | this.addInt8(value); 71 | } 72 | } 73 | addLoop(items, callback) { 74 | this.addUint16(items.length); 75 | for (const item of items) { 76 | callback(item); 77 | } 78 | } 79 | addString(str) { 80 | !this.sizeIsExact && this.testSize(2 + str.length * 2); 81 | this.addUint16(str.length); 82 | for (let i = 0, strLen = str.length; i < strLen; i++) { 83 | this.addUint16(str.charCodeAt(i)); 84 | } 85 | } 86 | addArrayBuffer(buff) { 87 | !this.sizeIsExact && this.testSize(2 + buff.byteLength); 88 | this.addUint16(buff.byteLength); 89 | this.uint.set(new Uint8Array(buff), this.curPosition); 90 | this.curPosition += buff.byteLength; 91 | } 92 | addSwitch(n, options) { 93 | this.addUint8(utils_1.switchType(n, options)); 94 | } 95 | addUint16(value) { 96 | !this.sizeIsExact && this.testSize(2); 97 | this.view.setUint16(this.curPosition, value); 98 | this.curPosition += 2; 99 | } 100 | addUint32(value) { 101 | !this.sizeIsExact && this.testSize(4); 102 | this.view.setUint32(this.curPosition, value); 103 | this.curPosition += 4; 104 | } 105 | addUint8(value) { 106 | !this.sizeIsExact && this.testSize(1); 107 | this.view.setUint8(this.curPosition, value); 108 | this.curPosition += 1; 109 | } 110 | buildBuffer() { 111 | return this.buffer; 112 | /* 113 | if (this.sizeIsExact && this.initialBufferSize > ArrayBufferBuilder.biggestCachableBuffer) { 114 | return this.buffer; 115 | } else { 116 | return this.buffer.slice(0, this.curPosition); 117 | } 118 | */ 119 | } 120 | testSize(added) { 121 | if (this.buffer.byteLength < this.curPosition + added) { 122 | this.buffer = transfer(this.buffer, this.buffer.byteLength * 4); 123 | this.view = new DataView(this.buffer); 124 | } 125 | } 126 | dispose() { 127 | if (this.sizeIsExact && this.initialBufferSize <= ArrayBufferBuilder.biggestCachableBufferSize) { 128 | if (!ArrayBufferBuilder.caches.has(this.initialBufferSize)) { 129 | const cache = ArrayBufferBuilder.caches.set(this.initialBufferSize, { 130 | buffer: this.buffer, 131 | view: this.view, 132 | uint: this.uint, 133 | }); 134 | /*for (let i = 0; i < this.buffer.byteLength; i + 4) { 135 | this.view.setUint32(i, 0); 136 | }*/ 137 | this.buffer = undefined; 138 | this.view = undefined; 139 | this.uint = undefined; 140 | return; 141 | } 142 | } 143 | this.buffer; 144 | } 145 | } 146 | exports.ArrayBufferBuilder = ArrayBufferBuilder; 147 | ArrayBufferBuilder.caches = new Map(); 148 | ArrayBufferBuilder.biggestCachableBufferSize = 0; //disabled 149 | class ArrayBufferReader { 150 | constructor(buffer) { 151 | this.dv = new DataView(buffer); 152 | this.index = 0; 153 | } 154 | done() { 155 | if (this.index !== this.dv.byteLength) { 156 | throw new Error('Bad input size'); 157 | } 158 | } 159 | loop(callback, size = '16') { 160 | let len; 161 | switch (size) { 162 | case '16': 163 | len = this.readUint16(); 164 | break; 165 | case '8': 166 | len = this.readUint8(); 167 | break; 168 | } 169 | const items = []; 170 | for (let i = 0; i < len; i++) { 171 | items.push(callback()); 172 | } 173 | return items; 174 | } 175 | readBits() { 176 | const int = this.readUint8(); 177 | return int 178 | .toString(2) 179 | .split('') 180 | .map((a) => a === '1') 181 | .slice(1); 182 | } 183 | readBoolean() { 184 | return this.readUint8() === 1; 185 | } 186 | readFloat32() { 187 | const result = this.dv.getFloat32(this.index); 188 | this.index += 4; 189 | return result; 190 | } 191 | readFloat64() { 192 | const result = this.dv.getFloat64(this.index); 193 | this.index += 8; 194 | return result; 195 | } 196 | readInt16() { 197 | const result = this.dv.getInt16(this.index); 198 | this.index += 2; 199 | return result; 200 | } 201 | readInt32() { 202 | const result = this.dv.getInt32(this.index); 203 | this.index += 4; 204 | return result; 205 | } 206 | readInt32Optional() { 207 | const result = this.dv.getInt32(this.index); 208 | this.index += 4; 209 | if (result === -1) { 210 | return undefined; 211 | } 212 | return result; 213 | } 214 | readInt8() { 215 | const result = this.dv.getInt8(this.index); 216 | this.index += 1; 217 | return result; 218 | } 219 | readInt8Optional() { 220 | const result = this.dv.getInt8(this.index); 221 | this.index += 1; 222 | if (result === -1) { 223 | return undefined; 224 | } 225 | return result; 226 | } 227 | readArrayBuffer() { 228 | const len = this.readUint16(); 229 | const buff = this.dv.buffer.slice(this.index, this.index + len); 230 | this.index += len; 231 | return buff; 232 | } 233 | readString() { 234 | const len = this.readUint16(); 235 | const strs = []; 236 | for (let i = 0; i < len; i++) { 237 | strs.push(String.fromCharCode(this.readUint16())); 238 | } 239 | return strs.join(''); 240 | } 241 | readUint16() { 242 | const result = this.dv.getUint16(this.index); 243 | this.index += 2; 244 | return result; 245 | } 246 | readUint32() { 247 | const result = this.dv.getUint32(this.index); 248 | this.index += 4; 249 | return result; 250 | } 251 | readUint8() { 252 | const result = this.dv.getUint8(this.index); 253 | this.index += 1; 254 | return result; 255 | } 256 | switch(callback) { 257 | const option = this.readUint8(); 258 | if (callback[option] === undefined) { 259 | throw new Error(`'Type not found', ${option}`); 260 | } 261 | return callback[option](); 262 | } 263 | } 264 | exports.ArrayBufferReader = ArrayBufferReader; 265 | const transfer = ArrayBuffer.transfer || 266 | ((source, length) => { 267 | if (!(source instanceof ArrayBuffer)) { 268 | throw new TypeError('Source must be an instance of ArrayBuffer'); 269 | } 270 | if (length <= source.byteLength) { 271 | return source.slice(0, length); 272 | } 273 | const sourceView = new Uint8Array(source); 274 | const destView = new Uint8Array(new ArrayBuffer(length)); 275 | destView.set(sourceView); 276 | return destView.buffer; 277 | }); 278 | //# sourceMappingURL=arrayBufferBuilder.js.map -------------------------------------------------------------------------------- /lib/arrayBufferBuilder.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"arrayBufferBuilder.js","sourceRoot":"","sources":["../src/arrayBufferBuilder.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | export { makeSchema, generateSchema, makeCustomSchema } from './schemaDefiner'; 2 | export { SafeSchema } from './schemaDefinerTypes'; 3 | //# sourceMappingURL=index.d.ts.map -------------------------------------------------------------------------------- /lib/index.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,cAAc,EAAE,gBAAgB,EAAC,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAC,UAAU,EAAC,MAAM,sBAAsB,CAAC"} -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.makeCustomSchema = exports.generateSchema = exports.makeSchema = void 0; 4 | var schemaDefiner_1 = require("./schemaDefiner"); 5 | Object.defineProperty(exports, "makeSchema", { enumerable: true, get: function () { return schemaDefiner_1.makeSchema; } }); 6 | Object.defineProperty(exports, "generateSchema", { enumerable: true, get: function () { return schemaDefiner_1.generateSchema; } }); 7 | Object.defineProperty(exports, "makeCustomSchema", { enumerable: true, get: function () { return schemaDefiner_1.makeCustomSchema; } }); 8 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /lib/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,iDAA6E;AAArE,2GAAA,UAAU,OAAA;AAAE,+GAAA,cAAc,OAAA;AAAE,iHAAA,gBAAgB,OAAA"} -------------------------------------------------------------------------------- /lib/schemaDefiner.d.ts: -------------------------------------------------------------------------------- 1 | import { CustomSchemaTypes, SafeSchema } from './schemaDefinerTypes'; 2 | export declare function generateSchema(schema: SafeSchema, customSchema?: TCustom): { 3 | toBuffer: (value: T) => ArrayBuffer; 4 | fromBuffer: (value: ArrayBuffer | ArrayBufferLike) => T; 5 | }; 6 | export declare function makeCustomSchema(t: CustomSchemaTypes): CustomSchemaTypes; 7 | export declare function makeSchema>(t: SafeSchema): SafeSchema; 8 | //# sourceMappingURL=schemaDefiner.d.ts.map -------------------------------------------------------------------------------- /lib/schemaDefiner.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"schemaDefiner.d.ts","sourceRoot":"","sources":["../src/schemaDefiner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,iBAAiB,EAAE,UAAU,EAAC,MAAM,sBAAsB,CAAC;AAsHzF,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,GAAG,KAAK,EAC/C,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,OAAO,CAAC,EAC9B,YAAY,CAAC,EAAE,OAAO,GACrB;IAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,WAAW,CAAC;IAAC,UAAU,EAAE,CAAC,KAAK,EAAE,WAAW,GAAG,eAAe,KAAK,CAAC,CAAA;CAAC,CAmBhG;AAiVD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAEjF;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC,EAAE,OAAO,CAAC,CAEnH"} -------------------------------------------------------------------------------- /lib/schemaDefiner.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.makeSchema = exports.makeCustomSchema = exports.generateSchema = void 0; 4 | const utils_1 = require("./utils"); 5 | const arrayBufferBuilder_1 = require("./arrayBufferBuilder"); 6 | function generateAdderFunction(schema, customSchema = {}) { 7 | const objectMaps = []; 8 | let code = buildAdderFunction(schema, 'value', (map) => { 9 | const id = objectMaps.length; 10 | objectMaps.push(`const map${id}=${map}`); 11 | return `map${id}`; 12 | }, customSchema); 13 | // language=JavaScript 14 | code = ` 15 | (buff, value,customSchema)=>{ 16 | ${objectMaps.join(';\n')} 17 | ${code} 18 | return buff.buildBuffer() 19 | }`; 20 | // tslint:disable no-eval 21 | return eval(code); 22 | } 23 | function generateAdderSizeFunction(schema, customSchema = {}) { 24 | const objectMaps = []; 25 | let code = buildAdderSizeFunction(schema, 'value', (map) => { 26 | const id = objectMaps.length; 27 | objectMaps.push(`const map${id}=${map}`); 28 | return `map${id}`; 29 | }, customSchema); 30 | // language=JavaScript 31 | code = ` 32 | var sum=(items)=>{ 33 | let c=0; 34 | for (let i = 0; i < items.length; i++) { 35 | c+=items[i]; 36 | } 37 | return c; 38 | }; 39 | (value,customSchema)=>{ 40 | ${objectMaps.join(';\n')} 41 | return (${code}0); 42 | }`; 43 | // tslint:disable no-eval 44 | return eval(code); 45 | } 46 | function generateReaderFunction(schema, customSchema = {}) { 47 | const objectMaps = []; 48 | let code = buildReaderFunction(schema, (map) => { 49 | const id = objectMaps.length; 50 | objectMaps.push(`const map${id}=${map}`); 51 | return `map${id}`; 52 | }, customSchema); 53 | // language=JavaScript 54 | code = ` 55 | function range(len, callback) { 56 | let items = []; 57 | for (let i = 0; i < len; i++) { 58 | items.push(callback()); 59 | } 60 | return items; 61 | } 62 | function lookup(id, obj) { 63 | return obj[id](); 64 | } 65 | function lookupEnum(id, obj) { 66 | return obj[id]; 67 | } 68 | function bitmask(mask, obj) { 69 | const result = {}; 70 | for (let i = 0; i < mask.length; i++) { 71 | result[obj[i]] = !!mask[i]; 72 | } 73 | return result; 74 | } 75 | 76 | (reader,customSchema)=>{ 77 | ${objectMaps.join(';\n')} 78 | return (${code}) 79 | }`; 80 | // tslint:disable no-eval 81 | return eval(code); 82 | } 83 | function generateSchema(schema, customSchema) { 84 | const readerFunction = generateReaderFunction(schema, customSchema); 85 | const adderFunction = generateAdderFunction(schema, customSchema); 86 | const adderSizeFunction = generateAdderSizeFunction(schema, customSchema); 87 | let generator = { 88 | readerFunction, 89 | adderFunction, 90 | adderSizeFunction, 91 | customSchema: customSchema !== null && customSchema !== void 0 ? customSchema : {}, 92 | }; 93 | return { 94 | toBuffer: (value) => { 95 | return toBuffer(value, generator); 96 | }, 97 | fromBuffer: (value) => { 98 | return fromBuffer(value, generator); 99 | }, 100 | }; 101 | } 102 | exports.generateSchema = generateSchema; 103 | function toBuffer(value, generator) { 104 | const size = generator.adderSizeFunction(value, generator.customSchema); 105 | const arrayBufferBuilder = new arrayBufferBuilder_1.ArrayBufferBuilder(size, true); 106 | const result = generator.adderFunction(arrayBufferBuilder, value, generator.customSchema); 107 | arrayBufferBuilder.dispose(); 108 | return result; 109 | } 110 | function fromBuffer(buffer, generator) { 111 | return generator.readerFunction(new arrayBufferBuilder_1.ArrayBufferReader(buffer), generator.customSchema); 112 | } 113 | function buildAdderFunction(schema, fieldName, addMap, customSchema) { 114 | if (typeof schema === 'string' && customSchema[schema]) { 115 | return `customSchema['${schema}'].write(${fieldName},buff);\n`; 116 | } 117 | switch (schema) { 118 | case 'arrayBuffer': 119 | return `buff.addArrayBuffer(${fieldName});\n`; 120 | case 'uint8': 121 | return `buff.addUint8(${fieldName});\n`; 122 | case 'uint16': 123 | return `buff.addUint16(${fieldName});\n`; 124 | case 'uint32': 125 | return `buff.addUint32(${fieldName});\n`; 126 | case 'int8': 127 | return `buff.addInt8(${fieldName});\n`; 128 | case 'int16': 129 | return `buff.addInt16(${fieldName});\n`; 130 | case 'int32': 131 | return `buff.addInt32(${fieldName});\n`; 132 | case 'float32': 133 | return `buff.addFloat32(${fieldName});\n`; 134 | case 'float64': 135 | return `buff.addFloat64(${fieldName});\n`; 136 | case 'boolean': 137 | return `buff.addBoolean(${fieldName});\n`; 138 | case 'string': 139 | return `buff.addString(${fieldName});\n`; 140 | default: 141 | utils_1.assertType(schema); 142 | switch (schema.flag) { 143 | case 'enum': { 144 | return `switch(${fieldName}){ 145 | ${utils_1.safeKeysExclude(schema, 'flag') 146 | .map((key) => `case '${key}':buff.addUint8(${schema[key]});break;`) 147 | .join('\n')} 148 | }`; 149 | } 150 | case 'number-enum': { 151 | return `switch(${fieldName}){ 152 | ${utils_1.safeKeysExclude(schema, 'flag') 153 | .map((key) => `case ${key}:buff.addUint8(${schema[key]});break;`) 154 | .join('\n')} 155 | }`; 156 | } 157 | case 'bitmask': { 158 | return `buff.addBits(...[ 159 | ${utils_1.safeKeysExclude(schema, 'flag') 160 | .map((key) => `${fieldName}['${key}'],`) 161 | .join('\n')} 162 | ]);`; 163 | } 164 | case 'array-uint8': { 165 | const noPeriodsFieldName = fieldName.replace(/\["/g, '_').replace(/"]/g, ''); 166 | return ` 167 | buff.addUint8(${fieldName}.length); 168 | for (const ${noPeriodsFieldName}Element of ${fieldName}) { 169 | ${buildAdderFunction(schema.elements, noPeriodsFieldName + 'Element', addMap, customSchema)} 170 | }`; 171 | } 172 | case 'array-uint16': { 173 | const noPeriodsFieldName = fieldName.replace(/\["/g, '_').replace(/"]/g, ''); 174 | return ` 175 | buff.addUint16(${fieldName}.length); 176 | for (const ${noPeriodsFieldName}Element of ${fieldName}) { 177 | ${buildAdderFunction(schema.elements, noPeriodsFieldName + 'Element', addMap, customSchema)} 178 | }`; 179 | } 180 | case 'array-uint32': { 181 | const noPeriodsFieldName = fieldName.replace(/\["/g, '_').replace(/"]/g, ''); 182 | return ` 183 | buff.addUint32(${fieldName}.length); 184 | for (const ${noPeriodsFieldName}Element of ${fieldName}) { 185 | ${buildAdderFunction(schema.elements, noPeriodsFieldName + 'Element', addMap, customSchema)} 186 | }`; 187 | } 188 | case 'type-lookup': { 189 | return `switch(${fieldName}.type){ 190 | ${Object.keys(schema.elements) 191 | .map((key, index) => `case '${key}':{ 192 | buff.addUint8(${index}); 193 | ${buildAdderFunction(schema.elements[key], fieldName, addMap, customSchema)}; 194 | break; 195 | }`) 196 | .join('\n')} 197 | }`; 198 | } 199 | case 'optional': { 200 | return `buff.addBoolean(${fieldName}!==undefined); 201 | if(${fieldName}!==undefined){ 202 | ${buildAdderFunction(schema.element, fieldName, addMap, customSchema)} 203 | }`; 204 | } 205 | case undefined: 206 | utils_1.assertType(schema); 207 | let result = ''; 208 | for (const key of Object.keys(schema)) { 209 | const currentSchemaElement = schema[key]; 210 | result += buildAdderFunction(currentSchemaElement, `${fieldName}["${key}"]`, addMap, customSchema) + '\n'; 211 | } 212 | return result; 213 | } 214 | } 215 | throw new Error('Buffer error'); 216 | } 217 | function buildAdderSizeFunction(schema, fieldName, addMap, customSchema) { 218 | if (typeof schema === 'string' && customSchema[schema]) { 219 | return `customSchema['${schema}'].size(${fieldName})+`; 220 | } 221 | switch (schema) { 222 | case 'arrayBuffer': 223 | return `2+${fieldName}.byteLength+`; 224 | case 'uint8': 225 | return `1+`; 226 | case 'uint16': 227 | return `2+`; 228 | case 'uint32': 229 | return `4+`; 230 | case 'int8': 231 | return `1+`; 232 | case 'int16': 233 | return `2+`; 234 | case 'int32': 235 | return `4+`; 236 | case 'float32': 237 | return `4+`; 238 | case 'float64': 239 | return `8+`; 240 | case 'boolean': 241 | return `1+`; 242 | case 'string': 243 | return `2+${fieldName}.length*2+`; 244 | default: 245 | utils_1.assertType(schema); 246 | switch (schema.flag) { 247 | case 'enum': { 248 | return `1+`; 249 | } 250 | case 'number-enum': { 251 | return `1+`; 252 | } 253 | case 'bitmask': { 254 | return `1+`; 255 | } 256 | case 'optional': { 257 | return `1+(${fieldName}!==undefined?(${buildAdderSizeFunction(schema.element, fieldName, addMap, customSchema)}0):0)+`; 258 | } 259 | case 'array-uint8': 260 | case 'array-uint16': 261 | case 'array-uint32': { 262 | const noPeriodsFieldName = fieldName.replace(/\["/g, '_').replace(/"]/g, ''); 263 | let size; 264 | switch (schema.flag) { 265 | case 'array-uint8': 266 | size = 1; 267 | break; 268 | case 'array-uint16': 269 | size = 2; 270 | break; 271 | case 'array-uint32': 272 | size = 4; 273 | break; 274 | } 275 | return `${size}+sum(${fieldName}.map(${noPeriodsFieldName + 'Element'}=>(${buildAdderSizeFunction(schema.elements, noPeriodsFieldName + 'Element', addMap, customSchema)}0)))+`; 276 | } 277 | case 'type-lookup': { 278 | let map = '{\n'; 279 | let index = 0; 280 | for (const key of Object.keys(schema.elements)) { 281 | map += `${key}:()=>1+${buildAdderSizeFunction(schema.elements[key], fieldName, addMap, customSchema)}0,`; 282 | index++; 283 | } 284 | map += '}'; 285 | return `(${map})[${fieldName}.type]()+`; 286 | } 287 | case undefined: 288 | utils_1.assertType(schema); 289 | let result = ''; 290 | for (const key of Object.keys(schema)) { 291 | const currentSchemaElement = schema[key]; 292 | result += buildAdderSizeFunction(currentSchemaElement, `${fieldName}["${key}"]`, addMap, customSchema) + ''; 293 | } 294 | return result + '0+'; 295 | } 296 | } 297 | throw new Error('Buffer error'); 298 | } 299 | function buildReaderFunction(schema, addMap, customSchema, injectField) { 300 | if (typeof schema === 'string' && customSchema[schema]) { 301 | return `customSchema['${schema}'].read(reader)`; 302 | } 303 | switch (schema) { 304 | case 'arrayBuffer': 305 | return 'reader.readArrayBuffer()'; 306 | case 'uint8': 307 | return 'reader.readUint8()'; 308 | case 'uint16': 309 | return 'reader.readUint16()'; 310 | case 'uint32': 311 | return 'reader.readUint32()'; 312 | case 'int8': 313 | return 'reader.readInt8()'; 314 | case 'int16': 315 | return 'reader.readInt16()'; 316 | case 'int32': 317 | return 'reader.readInt32()'; 318 | case 'float32': 319 | return 'reader.readFloat32()'; 320 | case 'float64': 321 | return 'reader.readFloat64()'; 322 | case 'boolean': 323 | return 'reader.readBoolean()'; 324 | case 'string': 325 | return 'reader.readString()'; 326 | default: 327 | utils_1.assertType(schema); 328 | switch (schema.flag) { 329 | case 'enum': { 330 | return `lookupEnum(reader.readUint8(),{${utils_1.safeKeysExclude(schema, 'flag') 331 | .map((key) => `['${schema[key]}']:'${key}'`) 332 | .join(',')}})`; 333 | } 334 | case 'number-enum': { 335 | return `parseFloat(lookupEnum(reader.readUint8(),{${utils_1.safeKeysExclude(schema, 'flag') 336 | .map((key) => `[${schema[key]}]:'${key}'`) 337 | .join(',')}}))`; 338 | } 339 | case 'bitmask': { 340 | return `bitmask(reader.readBits(), {${utils_1.safeKeysExclude(schema, 'flag') 341 | .map((key) => `['${schema[key]}']:'${key}'`) 342 | .join(',')}})`; 343 | } 344 | case 'array-uint8': { 345 | return `range(reader.readUint8(),()=>(${buildReaderFunction(schema.elements, addMap, customSchema)}))`; 346 | } 347 | case 'array-uint16': { 348 | return `range(reader.readUint16(),()=>(${buildReaderFunction(schema.elements, addMap, customSchema)}))`; 349 | } 350 | case 'array-uint32': { 351 | return `range(reader.readUint32(),()=>(${buildReaderFunction(schema.elements, addMap, customSchema)}))`; 352 | } 353 | case 'optional': { 354 | return `reader.readBoolean()? 355 | ${buildReaderFunction(schema.element, addMap, customSchema)} 356 | :undefined`; 357 | } 358 | case 'type-lookup': { 359 | let map = '{\n'; 360 | let index = 0; 361 | for (const key of Object.keys(schema.elements)) { 362 | map += `${index}:()=>( 363 | ${buildReaderFunction(schema.elements[key], addMap, customSchema, `type: '${key}'`)} 364 | ),\n`; 365 | index++; 366 | } 367 | map += '}\n'; 368 | const newMapId = addMap(map); 369 | return `lookup(reader.readUint8(),${newMapId})\n`; 370 | } 371 | case undefined: { 372 | utils_1.assertType(schema); 373 | let str = '{'; 374 | if (injectField) { 375 | str += `${injectField},\n`; 376 | } 377 | if (typeof schema !== 'object') { 378 | throw new Error('Buffer error'); 379 | } 380 | for (const key of Object.keys(schema)) { 381 | const currentSchemaElement = schema[key]; 382 | str += `['${key}'] : ${buildReaderFunction(currentSchemaElement, addMap, customSchema)},\n`; 383 | } 384 | str += '}'; 385 | return str; 386 | } 387 | } 388 | } 389 | throw new Error('Buffer error'); 390 | } 391 | function makeCustomSchema(t) { 392 | return t; 393 | } 394 | exports.makeCustomSchema = makeCustomSchema; 395 | function makeSchema(t) { 396 | return t; 397 | } 398 | exports.makeSchema = makeSchema; 399 | //# sourceMappingURL=schemaDefiner.js.map -------------------------------------------------------------------------------- /lib/schemaDefiner.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"schemaDefiner.js","sourceRoot":"","sources":["../src/schemaDefiner.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /lib/schemaDefinerTypes.d.ts: -------------------------------------------------------------------------------- 1 | import { ArrayBufferBuilder, ArrayBufferReader } from './arrayBufferBuilder'; 2 | declare type Discriminate = T extends { 3 | [field in TField]: TValue; 4 | } ? T : never; 5 | export declare type SafeSchemaEnum = Record & { 6 | flag: 'enum'; 7 | }; 8 | export declare type SafeSchemaNumberEnum = { 9 | [key in T]: number; 10 | } & { 11 | flag: 'number-enum'; 12 | }; 13 | export declare type SafeSchemaBitmask = { 14 | [keyT in keyof T]-?: number; 15 | } & { 16 | flag: 'bitmask'; 17 | }; 18 | export declare type SafeSchemaArray = { 19 | elements: TElements; 20 | flag: 'array-uint8' | 'array-uint16' | 'array-uint32'; 21 | }; 22 | export declare type SafeSchemaTypeLookupElements = { 25 | elements: { 26 | [key in TElements['type']]: SafeSchemaTypeLookup; 27 | }; 28 | flag: 'type-lookup'; 29 | }; 30 | export declare type SafeSchemaTypeLookup = SafeSchemaSimpleObject, 'type'>, TCustom>; 33 | export declare type SafeSchemaTypeElement = SafeSchemaSimpleObject, TCustom>; 36 | export declare type SafeSchemaSimpleObject = { 37 | [keyT in OptionalPropertyOf]: { 38 | element: SafeSchema[keyT], TCustom>; 39 | flag: 'optional'; 40 | } | keyof TCustom; 41 | } & { 42 | [keyT in RequiredPropertyOf]: SafeSchema[keyT], TCustom> | keyof TCustom; 43 | }; 44 | declare type OptionalPropertyOf = Exclude<{ 45 | [K in keyof T]: T extends Record ? never : K; 46 | }[keyof T], undefined>; 47 | declare type RequiredPropertyOf = Exclude<{ 48 | [K in keyof T]: T extends Record ? K : never; 49 | }[keyof T], undefined>; 50 | declare type SafeSchemaSimple = T extends string ? 'string' : T extends number ? 'uint8' | 'uint16' | 'uint32' | 'int8' | 'int16' | 'int32' | 'float32' | 'float64' : T extends boolean ? 'boolean' : never; 51 | declare type Cast = T extends TCast ? T : never; 52 | export declare type SafeSchema = [string extends T ? 1 : 0, T extends string ? 1 : 0] extends [0, 1] ? SafeSchemaEnum> : [number extends T ? 1 : 0, T extends number ? 1 : 0] extends [0, 1] ? SafeSchemaNumberEnum> : T extends string | boolean | number ? SafeSchemaSimple : T extends ArrayBuffer ? 'arrayBuffer' : T extends Array ? T[number] extends string | boolean | number ? SafeSchemaArray> : T[number] extends ArrayBuffer ? SafeSchemaArray<'arrayBuffer'> : T[number] extends { 53 | type: string; 54 | } ? SafeSchemaArray> | SafeSchemaArray> : SafeSchemaArray> : T extends { 55 | [key in keyof T]: boolean; 56 | } ? SafeSchemaBitmask | SafeSchemaSimpleObject : T extends {} ? T extends { 57 | type: string; 58 | } ? SafeSchemaTypeLookupElements : SafeSchemaSimpleObject : never; 59 | export declare type CustomSchemaTypes = { 60 | [key in keyof TCustom]: { 61 | read: (buffer: ArrayBufferReader) => TCustom[key]; 62 | write: (model: TCustom[key], buffer: ArrayBufferBuilder) => void; 63 | size: (model: TCustom[key]) => number; 64 | }; 65 | }; 66 | export declare type ABFlags = { 67 | flag: 'enum'; 68 | } | { 69 | flag: 'number-enum'; 70 | } | { 71 | element: any; 72 | flag: 'optional'; 73 | } | { 74 | flag: 'bitmask'; 75 | } | { 76 | elements: any; 77 | flag: 'array-uint32'; 78 | } | { 79 | elements: any; 80 | flag: 'array-uint16'; 81 | } | { 82 | elements: any; 83 | flag: 'array-uint8'; 84 | } | { 85 | elements: { 86 | [key: string]: ABSchemaDef; 87 | }; 88 | flag: 'type-lookup'; 89 | } | ({ 90 | flag: undefined; 91 | } & { 92 | [key: string]: any; 93 | }); 94 | export declare type ABSchemaDef = ABFlags | string; 95 | export {}; 96 | //# sourceMappingURL=schemaDefinerTypes.d.ts.map -------------------------------------------------------------------------------- /lib/schemaDefinerTypes.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"schemaDefinerTypes.d.ts","sourceRoot":"","sources":["../src/schemaDefinerTypes.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /lib/schemaDefinerTypes.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | //# sourceMappingURL=schemaDefinerTypes.js.map -------------------------------------------------------------------------------- /lib/schemaDefinerTypes.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"schemaDefinerTypes.js","sourceRoot":"","sources":["../src/schemaDefinerTypes.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /lib/utils.d.ts: -------------------------------------------------------------------------------- 1 | export declare function objectSafeKeys(obj: T): (keyof T)[]; 2 | export declare function assert(assertion: boolean): asserts assertion; 3 | export declare function assertType(assertion: any): asserts assertion is T; 4 | export declare function safeKeysExclude(obj: T, exclude: TExclude): Exclude[]; 5 | export declare function switchType(n: TType, options: { 6 | [key in TType]: TResult; 7 | }): TResult; 8 | //# sourceMappingURL=utils.d.ts.map -------------------------------------------------------------------------------- /lib/utils.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,cAAc,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,CAErD;AAED,wBAAgB,MAAM,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAG;AAEhE,wBAAgB,UAAU,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,CAAG;AAExE,wBAAgB,eAAe,CAAC,CAAC,EAAE,QAAQ,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,EAAE,CAEpH;AAED,wBAAgB,UAAU,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,EAAE,OAAO,EAC/D,CAAC,EAAE,KAAK,EACR,OAAO,EAAE;KAAG,GAAG,IAAI,KAAK,GAAG,OAAO;CAAE,GACnC,OAAO,CAKT"} -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.switchType = exports.safeKeysExclude = exports.assertType = exports.assert = exports.objectSafeKeys = void 0; 4 | function objectSafeKeys(obj) { 5 | return Object.keys(obj); 6 | } 7 | exports.objectSafeKeys = objectSafeKeys; 8 | function assert(assertion) { } 9 | exports.assert = assert; 10 | function assertType(assertion) { } 11 | exports.assertType = assertType; 12 | function safeKeysExclude(obj, exclude) { 13 | return Object.keys(obj).filter((key) => key !== exclude); 14 | } 15 | exports.safeKeysExclude = safeKeysExclude; 16 | function switchType(n, options) { 17 | if (options[n] === undefined) { 18 | throw new Error(`'Type not found', ${n}, ${JSON.stringify(options)}`); 19 | } 20 | return options[n]; 21 | } 22 | exports.switchType = switchType; 23 | //# sourceMappingURL=utils.js.map -------------------------------------------------------------------------------- /lib/utils.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;AAAA,SAAgB,cAAc,CAAI,GAAM;IACtC,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAgB,CAAC;AACzC,CAAC;AAFD,wCAEC;AAED,SAAgB,MAAM,CAAC,SAAkB,IAAsB,CAAC;AAAhE,wBAAgE;AAEhE,SAAgB,UAAU,CAAI,SAAc,IAA2B,CAAC;AAAxE,gCAAwE;AAExE,SAAgB,eAAe,CAA8B,GAAM,EAAE,OAAiB;IACpF,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,OAAO,CAAiC,CAAC;AAC3F,CAAC;AAFD,0CAEC;AAED,SAAgB,UAAU,CACxB,CAAQ,EACR,OAAoC;IAEpC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE;QAC5B,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;KACvE;IACD,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AARD,gCAQC"} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "safe-schema", 3 | "version": "1.1.7", 4 | "homepage": "https://github.com/dested/safe-schema", 5 | "author": "Salvatore ", 6 | "main": "./lib/index.js", 7 | "types": "./lib/index.d.ts", 8 | "publishConfig": { 9 | "access": "public" 10 | }, 11 | "scripts": { 12 | "test": "jest", 13 | "build": "tsc", 14 | "watch": "tsc -w --p ./tsconfig.test.json" 15 | }, 16 | "dependencies": { 17 | }, 18 | "devDependencies": { 19 | "typescript": "^4.0.5", 20 | "prettier": "^2.1.2", 21 | "@types/jest": "^22.2.3", 22 | "jest": "^24.9.0", 23 | "ts-jest": "^24.3.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/arrayBufferBuilder.ts: -------------------------------------------------------------------------------- 1 | import {switchType} from './utils'; 2 | 3 | export class ArrayBufferBuilder { 4 | buffer: ArrayBuffer; 5 | uint: Uint8Array; 6 | curPosition = 0; 7 | view: DataView; 8 | 9 | static caches = new Map(); 10 | static biggestCachableBufferSize = 0; //disabled 11 | 12 | constructor(private initialBufferSize: number = 50, private sizeIsExact: boolean = false) { 13 | if (sizeIsExact && initialBufferSize <= ArrayBufferBuilder.biggestCachableBufferSize) { 14 | if (ArrayBufferBuilder.caches.has(initialBufferSize)) { 15 | const cache = ArrayBufferBuilder.caches.get(initialBufferSize); 16 | this.buffer = cache.buffer; 17 | this.view = cache.view; 18 | this.uint = cache.uint; 19 | ArrayBufferBuilder.caches.delete(initialBufferSize); 20 | return; 21 | } 22 | } 23 | 24 | this.buffer = new ArrayBuffer(initialBufferSize); 25 | this.uint = new Uint8Array(this.buffer); 26 | this.view = new DataView(this.buffer); 27 | } 28 | 29 | addBits(...bools: boolean[]) { 30 | this.addUint8(parseInt('1' + bools.map((a) => (a ? '1' : '0')).join(''), 2)); 31 | } 32 | addBoolean(value: boolean) { 33 | !this.sizeIsExact && this.testSize(1); 34 | this.view.setUint8(this.curPosition, value ? 1 : 0); 35 | this.curPosition += 1; 36 | } 37 | 38 | addFloat32(value: number) { 39 | !this.sizeIsExact && this.testSize(4); 40 | this.view.setFloat32(this.curPosition, value); 41 | this.curPosition += 4; 42 | } 43 | 44 | addFloat64(value: number) { 45 | !this.sizeIsExact && this.testSize(8); 46 | this.view.setFloat64(this.curPosition, value); 47 | this.curPosition += 8; 48 | } 49 | 50 | addInt16(value: number) { 51 | !this.sizeIsExact && this.testSize(2); 52 | this.view.setInt16(this.curPosition, value); 53 | this.curPosition += 2; 54 | } 55 | 56 | addInt32(value: number) { 57 | !this.sizeIsExact && this.testSize(4); 58 | this.view.setInt32(this.curPosition, value); 59 | this.curPosition += 4; 60 | } 61 | 62 | addInt32Optional(value?: number) { 63 | if (value === undefined) { 64 | this.addInt32(-1); 65 | } else { 66 | this.addInt32(value); 67 | } 68 | } 69 | 70 | addInt8(value: number) { 71 | !this.sizeIsExact && this.testSize(1); 72 | this.view.setInt8(this.curPosition, value); 73 | this.curPosition += 1; 74 | } 75 | 76 | addInt8Optional(value?: number) { 77 | if (value === undefined) { 78 | this.addInt8(-1); 79 | } else { 80 | this.addInt8(value); 81 | } 82 | } 83 | addLoop(items: T[], callback: (t: T) => void) { 84 | this.addUint16(items.length); 85 | for (const item of items) { 86 | callback(item); 87 | } 88 | } 89 | 90 | addString(str: string) { 91 | !this.sizeIsExact && this.testSize(2 + str.length * 2); 92 | this.addUint16(str.length); 93 | for (let i = 0, strLen = str.length; i < strLen; i++) { 94 | this.addUint16(str.charCodeAt(i)); 95 | } 96 | } 97 | 98 | addArrayBuffer(buff: ArrayBuffer) { 99 | !this.sizeIsExact && this.testSize(2 + buff.byteLength); 100 | this.addUint16(buff.byteLength); 101 | this.uint.set(new Uint8Array(buff), this.curPosition); 102 | this.curPosition += buff.byteLength; 103 | } 104 | 105 | addSwitch(n: TType, options: {[key in TType]: TResult}) { 106 | this.addUint8(switchType(n, options)); 107 | } 108 | 109 | addUint16(value: number) { 110 | !this.sizeIsExact && this.testSize(2); 111 | this.view.setUint16(this.curPosition, value); 112 | this.curPosition += 2; 113 | } 114 | 115 | addUint32(value: number) { 116 | !this.sizeIsExact && this.testSize(4); 117 | this.view.setUint32(this.curPosition, value); 118 | this.curPosition += 4; 119 | } 120 | 121 | addUint8(value: number) { 122 | !this.sizeIsExact && this.testSize(1); 123 | this.view.setUint8(this.curPosition, value); 124 | this.curPosition += 1; 125 | } 126 | 127 | buildBuffer(): ArrayBuffer { 128 | return this.buffer; 129 | 130 | /* 131 | if (this.sizeIsExact && this.initialBufferSize > ArrayBufferBuilder.biggestCachableBuffer) { 132 | return this.buffer; 133 | } else { 134 | return this.buffer.slice(0, this.curPosition); 135 | } 136 | */ 137 | } 138 | 139 | testSize(added: number) { 140 | if (this.buffer.byteLength < this.curPosition + added) { 141 | this.buffer = transfer(this.buffer, this.buffer.byteLength * 4); 142 | this.view = new DataView(this.buffer); 143 | } 144 | } 145 | 146 | dispose() { 147 | if (this.sizeIsExact && this.initialBufferSize <= ArrayBufferBuilder.biggestCachableBufferSize) { 148 | if (!ArrayBufferBuilder.caches.has(this.initialBufferSize)) { 149 | const cache = ArrayBufferBuilder.caches.set(this.initialBufferSize, { 150 | buffer: this.buffer, 151 | view: this.view, 152 | uint: this.uint, 153 | }); 154 | /*for (let i = 0; i < this.buffer.byteLength; i + 4) { 155 | this.view.setUint32(i, 0); 156 | }*/ 157 | this.buffer = undefined; 158 | this.view = undefined; 159 | this.uint = undefined; 160 | return; 161 | } 162 | } 163 | this.buffer; 164 | } 165 | } 166 | 167 | export class ArrayBufferReader { 168 | private dv: DataView; 169 | private index: number; 170 | 171 | constructor(buffer: ArrayBuffer | ArrayBufferLike) { 172 | this.dv = new DataView(buffer); 173 | this.index = 0; 174 | } 175 | 176 | done() { 177 | if (this.index !== this.dv.byteLength) { 178 | throw new Error('Bad input size'); 179 | } 180 | } 181 | 182 | loop(callback: () => T, size: '16' | '8' = '16'): T[] { 183 | let len: number; 184 | switch (size) { 185 | case '16': 186 | len = this.readUint16(); 187 | break; 188 | case '8': 189 | len = this.readUint8(); 190 | break; 191 | } 192 | const items: T[] = []; 193 | for (let i = 0; i < len; i++) { 194 | items.push(callback()); 195 | } 196 | return items; 197 | } 198 | 199 | readBits(): boolean[] { 200 | const int = this.readUint8(); 201 | return int 202 | .toString(2) 203 | .split('') 204 | .map((a) => a === '1') 205 | .slice(1); 206 | } 207 | 208 | readBoolean() { 209 | return this.readUint8() === 1; 210 | } 211 | 212 | readFloat32(): number { 213 | const result = this.dv.getFloat32(this.index); 214 | this.index += 4; 215 | return result; 216 | } 217 | 218 | readFloat64(): number { 219 | const result = this.dv.getFloat64(this.index); 220 | this.index += 8; 221 | return result; 222 | } 223 | 224 | readInt16(): number { 225 | const result = this.dv.getInt16(this.index); 226 | this.index += 2; 227 | return result; 228 | } 229 | 230 | readInt32(): number { 231 | const result = this.dv.getInt32(this.index); 232 | this.index += 4; 233 | return result; 234 | } 235 | 236 | readInt32Optional(): number | undefined { 237 | const result = this.dv.getInt32(this.index); 238 | this.index += 4; 239 | if (result === -1) { 240 | return undefined; 241 | } 242 | return result; 243 | } 244 | 245 | readInt8(): number { 246 | const result = this.dv.getInt8(this.index); 247 | this.index += 1; 248 | return result; 249 | } 250 | readInt8Optional(): number | undefined { 251 | const result = this.dv.getInt8(this.index); 252 | this.index += 1; 253 | if (result === -1) { 254 | return undefined; 255 | } 256 | return result; 257 | } 258 | 259 | readArrayBuffer() { 260 | const len = this.readUint16(); 261 | const buff = this.dv.buffer.slice(this.index, this.index + len); 262 | this.index += len; 263 | return buff; 264 | } 265 | 266 | readString() { 267 | const len = this.readUint16(); 268 | const strs: string[] = []; 269 | for (let i = 0; i < len; i++) { 270 | strs.push(String.fromCharCode(this.readUint16())); 271 | } 272 | return strs.join(''); 273 | } 274 | 275 | readUint16(): number { 276 | const result = this.dv.getUint16(this.index); 277 | this.index += 2; 278 | return result; 279 | } 280 | 281 | readUint32(): number { 282 | const result = this.dv.getUint32(this.index); 283 | this.index += 4; 284 | return result; 285 | } 286 | 287 | readUint8(): number { 288 | const result = this.dv.getUint8(this.index); 289 | this.index += 1; 290 | return result; 291 | } 292 | 293 | switch(callback: {[key in TOptions]: () => TResult}): TResult { 294 | const option = this.readUint8() as TOptions; 295 | if (callback[option] === undefined) { 296 | throw new Error(`'Type not found', ${option}`); 297 | } 298 | return callback[option](); 299 | } 300 | } 301 | 302 | const transfer = 303 | (ArrayBuffer as any).transfer || 304 | ((source: ArrayBuffer, length: number) => { 305 | if (!(source instanceof ArrayBuffer)) { 306 | throw new TypeError('Source must be an instance of ArrayBuffer'); 307 | } 308 | if (length <= source.byteLength) { 309 | return source.slice(0, length); 310 | } 311 | const sourceView = new Uint8Array(source); 312 | const destView = new Uint8Array(new ArrayBuffer(length)); 313 | destView.set(sourceView); 314 | return destView.buffer; 315 | }); 316 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export {makeSchema, generateSchema, makeCustomSchema} from './schemaDefiner'; 2 | export {SafeSchema} from './schemaDefinerTypes'; 3 | -------------------------------------------------------------------------------- /src/schemaDefiner.ts: -------------------------------------------------------------------------------- 1 | import {ABFlags, ABSchemaDef, CustomSchemaTypes, SafeSchema} from './schemaDefinerTypes'; 2 | import {assertType, safeKeysExclude} from './utils'; 3 | import {ArrayBufferBuilder, ArrayBufferReader} from './arrayBufferBuilder'; 4 | 5 | type SchemaGenerator = { 6 | readerFunction: ReaderFunction; 7 | adderFunction: AdderFunction; 8 | adderSizeFunction: AdderSizeFunction; 9 | customSchema: CustomSchemaTypes; 10 | }; 11 | 12 | function generateAdderFunction( 13 | schema: SafeSchema, 14 | customSchema: TCustom = {} as any 15 | ): AdderFunction { 16 | const objectMaps: string[] = []; 17 | 18 | let code = buildAdderFunction( 19 | schema as any, 20 | 'value', 21 | (map) => { 22 | const id = objectMaps.length; 23 | objectMaps.push(`const map${id}=${map}`); 24 | return `map${id}`; 25 | }, 26 | customSchema as any 27 | ); 28 | 29 | // language=JavaScript 30 | code = ` 31 | (buff, value,customSchema)=>{ 32 | ${objectMaps.join(';\n')} 33 | ${code} 34 | return buff.buildBuffer() 35 | }`; 36 | // tslint:disable no-eval 37 | return eval(code); 38 | } 39 | function generateAdderSizeFunction( 40 | schema: SafeSchema, 41 | customSchema: TCustom = {} as any 42 | ): AdderSizeFunction { 43 | const objectMaps: string[] = []; 44 | let code = buildAdderSizeFunction( 45 | schema as any, 46 | 'value', 47 | (map) => { 48 | const id = objectMaps.length; 49 | objectMaps.push(`const map${id}=${map}`); 50 | return `map${id}`; 51 | }, 52 | customSchema as any 53 | ); 54 | 55 | // language=JavaScript 56 | code = ` 57 | var sum=(items)=>{ 58 | let c=0; 59 | for (let i = 0; i < items.length; i++) { 60 | c+=items[i]; 61 | } 62 | return c; 63 | }; 64 | (value,customSchema)=>{ 65 | ${objectMaps.join(';\n')} 66 | return (${code}0); 67 | }`; 68 | // tslint:disable no-eval 69 | return eval(code); 70 | } 71 | 72 | function generateReaderFunction( 73 | schema: SafeSchema, 74 | customSchema: TCustom = {} as any 75 | ): ReaderFunction { 76 | const objectMaps: string[] = []; 77 | 78 | let code = buildReaderFunction( 79 | schema as any, 80 | (map) => { 81 | const id = objectMaps.length; 82 | objectMaps.push(`const map${id}=${map}`); 83 | return `map${id}`; 84 | }, 85 | customSchema as any 86 | ); 87 | 88 | // language=JavaScript 89 | code = ` 90 | function range(len, callback) { 91 | let items = []; 92 | for (let i = 0; i < len; i++) { 93 | items.push(callback()); 94 | } 95 | return items; 96 | } 97 | function lookup(id, obj) { 98 | return obj[id](); 99 | } 100 | function lookupEnum(id, obj) { 101 | return obj[id]; 102 | } 103 | function bitmask(mask, obj) { 104 | const result = {}; 105 | for (let i = 0; i < mask.length; i++) { 106 | result[obj[i]] = !!mask[i]; 107 | } 108 | return result; 109 | } 110 | 111 | (reader,customSchema)=>{ 112 | ${objectMaps.join(';\n')} 113 | return (${code}) 114 | }`; 115 | // tslint:disable no-eval 116 | return eval(code); 117 | } 118 | 119 | export function generateSchema( 120 | schema: SafeSchema, 121 | customSchema?: TCustom 122 | ): {toBuffer: (value: T) => ArrayBuffer; fromBuffer: (value: ArrayBuffer | ArrayBufferLike) => T} { 123 | const readerFunction = generateReaderFunction(schema, customSchema); 124 | const adderFunction = generateAdderFunction(schema, customSchema); 125 | const adderSizeFunction = generateAdderSizeFunction(schema, customSchema); 126 | 127 | let generator = { 128 | readerFunction, 129 | adderFunction, 130 | adderSizeFunction, 131 | customSchema: customSchema ?? ({} as any), 132 | }; 133 | return { 134 | toBuffer: (value: T) => { 135 | return toBuffer(value, generator); 136 | }, 137 | fromBuffer: (value: ArrayBuffer | ArrayBufferLike) => { 138 | return fromBuffer(value, generator); 139 | }, 140 | }; 141 | } 142 | 143 | function toBuffer(value: T, generator: SchemaGenerator) { 144 | const size = generator.adderSizeFunction(value, generator.customSchema); 145 | const arrayBufferBuilder = new ArrayBufferBuilder(size, true); 146 | const result = generator.adderFunction(arrayBufferBuilder, value, generator.customSchema); 147 | arrayBufferBuilder.dispose(); 148 | return result; 149 | } 150 | 151 | function fromBuffer(buffer: ArrayBuffer | ArrayBufferLike, generator: SchemaGenerator): T { 152 | return generator.readerFunction(new ArrayBufferReader(buffer), generator.customSchema); 153 | } 154 | 155 | function buildAdderFunction( 156 | schema: ABSchemaDef, 157 | fieldName: string, 158 | addMap: (code: string) => string, 159 | customSchema: CustomSchemaTypes 160 | ): string { 161 | if (typeof schema === 'string' && customSchema[schema]) { 162 | return `customSchema['${schema}'].write(${fieldName},buff);\n`; 163 | } 164 | 165 | switch (schema) { 166 | case 'arrayBuffer': 167 | return `buff.addArrayBuffer(${fieldName});\n`; 168 | case 'uint8': 169 | return `buff.addUint8(${fieldName});\n`; 170 | case 'uint16': 171 | return `buff.addUint16(${fieldName});\n`; 172 | case 'uint32': 173 | return `buff.addUint32(${fieldName});\n`; 174 | case 'int8': 175 | return `buff.addInt8(${fieldName});\n`; 176 | case 'int16': 177 | return `buff.addInt16(${fieldName});\n`; 178 | case 'int32': 179 | return `buff.addInt32(${fieldName});\n`; 180 | case 'float32': 181 | return `buff.addFloat32(${fieldName});\n`; 182 | case 'float64': 183 | return `buff.addFloat64(${fieldName});\n`; 184 | case 'boolean': 185 | return `buff.addBoolean(${fieldName});\n`; 186 | case 'string': 187 | return `buff.addString(${fieldName});\n`; 188 | default: 189 | assertType(schema); 190 | switch (schema.flag) { 191 | case 'enum': { 192 | return `switch(${fieldName}){ 193 | ${safeKeysExclude(schema, 'flag') 194 | .map((key) => `case '${key}':buff.addUint8(${schema[key]});break;`) 195 | .join('\n')} 196 | }`; 197 | } 198 | case 'number-enum': { 199 | return `switch(${fieldName}){ 200 | ${safeKeysExclude(schema, 'flag') 201 | .map((key) => `case ${key}:buff.addUint8(${schema[key]});break;`) 202 | .join('\n')} 203 | }`; 204 | } 205 | case 'bitmask': { 206 | return `buff.addBits(...[ 207 | ${safeKeysExclude(schema, 'flag') 208 | .map((key) => `${fieldName}['${key}'],`) 209 | .join('\n')} 210 | ]);`; 211 | } 212 | 213 | case 'array-uint8': { 214 | const noPeriodsFieldName = fieldName.replace(/\["/g, '_').replace(/"]/g, ''); 215 | return ` 216 | buff.addUint8(${fieldName}.length); 217 | for (const ${noPeriodsFieldName}Element of ${fieldName}) { 218 | ${buildAdderFunction(schema.elements, noPeriodsFieldName + 'Element', addMap, customSchema)} 219 | }`; 220 | } 221 | case 'array-uint16': { 222 | const noPeriodsFieldName = fieldName.replace(/\["/g, '_').replace(/"]/g, ''); 223 | return ` 224 | buff.addUint16(${fieldName}.length); 225 | for (const ${noPeriodsFieldName}Element of ${fieldName}) { 226 | ${buildAdderFunction(schema.elements, noPeriodsFieldName + 'Element', addMap, customSchema)} 227 | }`; 228 | } 229 | case 'array-uint32': { 230 | const noPeriodsFieldName = fieldName.replace(/\["/g, '_').replace(/"]/g, ''); 231 | return ` 232 | buff.addUint32(${fieldName}.length); 233 | for (const ${noPeriodsFieldName}Element of ${fieldName}) { 234 | ${buildAdderFunction(schema.elements, noPeriodsFieldName + 'Element', addMap, customSchema)} 235 | }`; 236 | } 237 | case 'type-lookup': { 238 | return `switch(${fieldName}.type){ 239 | ${Object.keys(schema.elements) 240 | .map( 241 | (key, index) => 242 | `case '${key}':{ 243 | buff.addUint8(${index}); 244 | ${buildAdderFunction(schema.elements[key], fieldName, addMap, customSchema)}; 245 | break; 246 | }` 247 | ) 248 | .join('\n')} 249 | }`; 250 | } 251 | case 'optional': { 252 | return `buff.addBoolean(${fieldName}!==undefined); 253 | if(${fieldName}!==undefined){ 254 | ${buildAdderFunction(schema.element, fieldName, addMap, customSchema)} 255 | }`; 256 | } 257 | case undefined: 258 | assertType<{flag: undefined} & {[key: string]: any}>(schema); 259 | let result = ''; 260 | for (const key of Object.keys(schema)) { 261 | const currentSchemaElement = schema[key]; 262 | result += buildAdderFunction(currentSchemaElement, `${fieldName}["${key}"]`, addMap, customSchema) + '\n'; 263 | } 264 | return result; 265 | } 266 | } 267 | throw new Error('Buffer error'); 268 | } 269 | 270 | function buildAdderSizeFunction( 271 | schema: ABSchemaDef, 272 | fieldName: string, 273 | addMap: (code: string) => string, 274 | customSchema: CustomSchemaTypes 275 | ): string { 276 | if (typeof schema === 'string' && customSchema[schema]) { 277 | return `customSchema['${schema}'].size(${fieldName})+`; 278 | } 279 | 280 | switch (schema) { 281 | case 'arrayBuffer': 282 | return `2+${fieldName}.byteLength+`; 283 | case 'uint8': 284 | return `1+`; 285 | case 'uint16': 286 | return `2+`; 287 | case 'uint32': 288 | return `4+`; 289 | case 'int8': 290 | return `1+`; 291 | case 'int16': 292 | return `2+`; 293 | case 'int32': 294 | return `4+`; 295 | case 'float32': 296 | return `4+`; 297 | case 'float64': 298 | return `8+`; 299 | case 'boolean': 300 | return `1+`; 301 | case 'string': 302 | return `2+${fieldName}.length*2+`; 303 | default: 304 | assertType(schema); 305 | switch (schema.flag) { 306 | case 'enum': { 307 | return `1+`; 308 | } 309 | case 'number-enum': { 310 | return `1+`; 311 | } 312 | case 'bitmask': { 313 | return `1+`; 314 | } 315 | case 'optional': { 316 | return `1+(${fieldName}!==undefined?(${buildAdderSizeFunction( 317 | schema.element, 318 | fieldName, 319 | addMap, 320 | customSchema 321 | )}0):0)+`; 322 | } 323 | case 'array-uint8': 324 | case 'array-uint16': 325 | case 'array-uint32': { 326 | const noPeriodsFieldName = fieldName.replace(/\["/g, '_').replace(/"]/g, ''); 327 | let size: number; 328 | switch (schema.flag) { 329 | case 'array-uint8': 330 | size = 1; 331 | break; 332 | case 'array-uint16': 333 | size = 2; 334 | break; 335 | case 'array-uint32': 336 | size = 4; 337 | break; 338 | } 339 | return `${size}+sum(${fieldName}.map(${noPeriodsFieldName + 'Element'}=>(${buildAdderSizeFunction( 340 | schema.elements, 341 | noPeriodsFieldName + 'Element', 342 | addMap, 343 | customSchema 344 | )}0)))+`; 345 | } 346 | case 'type-lookup': { 347 | let map = '{\n'; 348 | let index = 0; 349 | for (const key of Object.keys(schema.elements)) { 350 | map += `${key}:()=>1+${buildAdderSizeFunction(schema.elements[key], fieldName, addMap, customSchema)}0,`; 351 | index++; 352 | } 353 | map += '}'; 354 | return `(${map})[${fieldName}.type]()+`; 355 | } 356 | case undefined: 357 | assertType<{flag: undefined} & {[key: string]: any}>(schema); 358 | let result = ''; 359 | for (const key of Object.keys(schema)) { 360 | const currentSchemaElement = schema[key]; 361 | result += buildAdderSizeFunction(currentSchemaElement, `${fieldName}["${key}"]`, addMap, customSchema) + ''; 362 | } 363 | return result + '0+'; 364 | } 365 | } 366 | throw new Error('Buffer error'); 367 | } 368 | 369 | function buildReaderFunction( 370 | schema: ABSchemaDef, 371 | addMap: (code: string) => string, 372 | customSchema: CustomSchemaTypes, 373 | injectField?: string 374 | ): any { 375 | if (typeof schema === 'string' && customSchema[schema]) { 376 | return `customSchema['${schema}'].read(reader)`; 377 | } 378 | switch (schema) { 379 | case 'arrayBuffer': 380 | return 'reader.readArrayBuffer()'; 381 | case 'uint8': 382 | return 'reader.readUint8()'; 383 | case 'uint16': 384 | return 'reader.readUint16()'; 385 | case 'uint32': 386 | return 'reader.readUint32()'; 387 | case 'int8': 388 | return 'reader.readInt8()'; 389 | case 'int16': 390 | return 'reader.readInt16()'; 391 | case 'int32': 392 | return 'reader.readInt32()'; 393 | case 'float32': 394 | return 'reader.readFloat32()'; 395 | case 'float64': 396 | return 'reader.readFloat64()'; 397 | case 'boolean': 398 | return 'reader.readBoolean()'; 399 | case 'string': 400 | return 'reader.readString()'; 401 | default: 402 | assertType(schema); 403 | switch (schema.flag) { 404 | case 'enum': { 405 | return `lookupEnum(reader.readUint8(),{${safeKeysExclude(schema, 'flag') 406 | .map((key) => `['${schema[key]}']:'${key}'`) 407 | .join(',')}})`; 408 | } 409 | case 'number-enum': { 410 | return `parseFloat(lookupEnum(reader.readUint8(),{${safeKeysExclude(schema, 'flag') 411 | .map((key) => `[${schema[key]}]:'${key}'`) 412 | .join(',')}}))`; 413 | } 414 | case 'bitmask': { 415 | return `bitmask(reader.readBits(), {${safeKeysExclude(schema, 'flag') 416 | .map((key) => `['${schema[key]}']:'${key}'`) 417 | .join(',')}})`; 418 | } 419 | 420 | case 'array-uint8': { 421 | return `range(reader.readUint8(),()=>(${buildReaderFunction(schema.elements, addMap, customSchema)}))`; 422 | } 423 | case 'array-uint16': { 424 | return `range(reader.readUint16(),()=>(${buildReaderFunction(schema.elements, addMap, customSchema)}))`; 425 | } 426 | case 'array-uint32': { 427 | return `range(reader.readUint32(),()=>(${buildReaderFunction(schema.elements, addMap, customSchema)}))`; 428 | } 429 | case 'optional': { 430 | return `reader.readBoolean()? 431 | ${buildReaderFunction(schema.element, addMap, customSchema)} 432 | :undefined`; 433 | } 434 | case 'type-lookup': { 435 | let map = '{\n'; 436 | let index = 0; 437 | for (const key of Object.keys(schema.elements)) { 438 | map += `${index}:()=>( 439 | ${buildReaderFunction(schema.elements[key], addMap, customSchema, `type: '${key}'`)} 440 | ),\n`; 441 | index++; 442 | } 443 | map += '}\n'; 444 | const newMapId = addMap(map); 445 | return `lookup(reader.readUint8(),${newMapId})\n`; 446 | } 447 | case undefined: { 448 | assertType<{flag: undefined} & {[key: string]: any}>(schema); 449 | let str = '{'; 450 | if (injectField) { 451 | str += `${injectField},\n`; 452 | } 453 | if (typeof schema !== 'object') { 454 | throw new Error('Buffer error'); 455 | } 456 | for (const key of Object.keys(schema)) { 457 | const currentSchemaElement = schema[key]; 458 | str += `['${key}'] : ${buildReaderFunction(currentSchemaElement, addMap, customSchema)},\n`; 459 | } 460 | str += '}'; 461 | return str; 462 | } 463 | } 464 | } 465 | throw new Error('Buffer error'); 466 | } 467 | 468 | type AdderSizeFunction = (value: T, customSchemaTypes: CustomSchemaTypes) => number; 469 | 470 | type AdderFunction = ( 471 | buff: ArrayBufferBuilder, 472 | value: T, 473 | customSchemaTypes: CustomSchemaTypes 474 | ) => ArrayBuffer; 475 | 476 | type ReaderFunction = (reader: ArrayBufferReader, customSchemaTypes: CustomSchemaTypes) => T; 477 | 478 | export function makeCustomSchema(t: CustomSchemaTypes): CustomSchemaTypes { 479 | return t; 480 | } 481 | 482 | export function makeSchema>(t: SafeSchema): SafeSchema { 483 | return t; 484 | } 485 | -------------------------------------------------------------------------------- /src/schemaDefinerTypes.ts: -------------------------------------------------------------------------------- 1 | import {ArrayBufferBuilder, ArrayBufferReader} from './arrayBufferBuilder'; 2 | 3 | type Discriminate = T extends {[field in TField]: TValue} 4 | ? T 5 | : never; 6 | export type SafeSchemaEnum = Record & {flag: 'enum'}; 7 | export type SafeSchemaNumberEnum = {[key in T]: number} & {flag: 'number-enum'}; 8 | export type SafeSchemaBitmask = {[keyT in keyof T]-?: number} & {flag: 'bitmask'}; 9 | export type SafeSchemaArray = {elements: TElements; flag: 'array-uint8' | 'array-uint16' | 'array-uint32'}; 10 | 11 | export type SafeSchemaTypeLookupElements = { 12 | elements: { 13 | [key in TElements['type']]: SafeSchemaTypeLookup; 14 | }; 15 | flag: 'type-lookup'; 16 | }; 17 | export type SafeSchemaTypeLookup< 18 | TItem extends {type: string}, 19 | TKey extends TItem['type'], 20 | TCustom 21 | > = SafeSchemaSimpleObject, 'type'>, TCustom>; 22 | 23 | export type SafeSchemaTypeElement = SafeSchemaSimpleObject< 24 | Omit, 25 | TCustom 26 | >; 27 | 28 | export type SafeSchemaSimpleObject = { 29 | [keyT in OptionalPropertyOf]: 30 | | {element: SafeSchema[keyT], TCustom>; flag: 'optional'} 31 | | keyof TCustom; 32 | } & 33 | { 34 | [keyT in RequiredPropertyOf]: SafeSchema[keyT], TCustom> | keyof TCustom; 35 | }; 36 | 37 | type OptionalPropertyOf = Exclude< 38 | { 39 | [K in keyof T]: T extends Record ? never : K; 40 | }[keyof T], 41 | undefined 42 | >; 43 | type RequiredPropertyOf = Exclude< 44 | { 45 | [K in keyof T]: T extends Record ? K : never; 46 | }[keyof T], 47 | undefined 48 | >; 49 | 50 | type SafeSchemaSimple = T extends string 51 | ? 'string' 52 | : T extends number 53 | ? 'uint8' | 'uint16' | 'uint32' | 'int8' | 'int16' | 'int32' | 'float32' | 'float64' 54 | : T extends boolean 55 | ? 'boolean' 56 | : never; 57 | 58 | type Cast = T extends TCast ? T : never; 59 | 60 | export type SafeSchema = [string extends T ? 1 : 0, T extends string ? 1 : 0] extends [0, 1] 61 | ? SafeSchemaEnum> 62 | : [number extends T ? 1 : 0, T extends number ? 1 : 0] extends [0, 1] 63 | ? SafeSchemaNumberEnum> 64 | : T extends string | boolean | number 65 | ? SafeSchemaSimple 66 | : T extends ArrayBuffer 67 | ? 'arrayBuffer' 68 | : T extends Array 69 | ? T[number] extends string | boolean | number 70 | ? SafeSchemaArray> 71 | : T[number] extends ArrayBuffer 72 | ? SafeSchemaArray<'arrayBuffer'> 73 | : T[number] extends {type: string} 74 | ? 75 | | SafeSchemaArray> 76 | | SafeSchemaArray> 77 | : SafeSchemaArray> 78 | : T extends {[key in keyof T]: boolean} 79 | ? SafeSchemaBitmask | SafeSchemaSimpleObject 80 | : T extends {} 81 | ? T extends {type: string} 82 | ? SafeSchemaTypeLookupElements 83 | : SafeSchemaSimpleObject 84 | : never; 85 | 86 | export type CustomSchemaTypes = { 87 | [key in keyof TCustom]: { 88 | read: (buffer: ArrayBufferReader) => TCustom[key]; 89 | write: (model: TCustom[key], buffer: ArrayBufferBuilder) => void; 90 | size: (model: TCustom[key]) => number; 91 | }; 92 | }; 93 | 94 | export type ABFlags = 95 | | {flag: 'enum'} 96 | | {flag: 'number-enum'} 97 | | {element: any; flag: 'optional'} 98 | | {flag: 'bitmask'} 99 | | {elements: any; flag: 'array-uint32'} 100 | | {elements: any; flag: 'array-uint16'} 101 | | {elements: any; flag: 'array-uint8'} 102 | | {elements: {[key: string]: ABSchemaDef}; flag: 'type-lookup'} 103 | | ({flag: undefined} & {[key: string]: any}); 104 | export type ABSchemaDef = ABFlags | string; 105 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | export function objectSafeKeys(obj: T): (keyof T)[] { 2 | return Object.keys(obj) as (keyof T)[]; 3 | } 4 | 5 | export function assert(assertion: boolean): asserts assertion {} 6 | 7 | export function assertType(assertion: any): asserts assertion is T {} 8 | 9 | export function safeKeysExclude(obj: T, exclude: TExclude): Exclude[] { 10 | return Object.keys(obj).filter((key) => key !== exclude) as Exclude[]; 11 | } 12 | 13 | export function switchType( 14 | n: TType, 15 | options: { [key in TType]: TResult } 16 | ): TResult { 17 | if (options[n] === undefined) { 18 | throw new Error(`'Type not found', ${n}, ${JSON.stringify(options)}`); 19 | } 20 | return options[n]; 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "target": "es2019", 5 | "lib": ["es2019", "DOM"], 6 | 7 | "module": "commonjs", 8 | "esModuleInterop": true, 9 | 10 | "declaration": true, 11 | "moduleResolution": "node", 12 | "declarationMap": true, 13 | "outDir": "./lib/", 14 | "sourceMap": true, 15 | "baseUrl": "./src" 16 | }, 17 | "exclude": [ 18 | "lib", 19 | "__tests__", 20 | "node_modules" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "target": "es2019", 5 | "lib": ["es2019", "DOM"], 6 | "module": "commonjs", 7 | "esModuleInterop": true, 8 | "noEmit": true, 9 | "moduleResolution": "node", 10 | "sourceMap": true, 11 | "baseUrl": "./src" 12 | }, 13 | "exclude": ["lib", "node_modules"] 14 | } 15 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended", 5 | "tslint-eslint-rules", 6 | "tslint-config-prettier" 7 | ], 8 | "jsRules": { 9 | }, 10 | "rules": { 11 | "radix": false, 12 | "member-ordering": false, 13 | "no-namespace": false, 14 | "no-reference": false, 15 | "trailing-comma": [ 16 | false, 17 | { 18 | "multiline": { 19 | "objects": "always", 20 | "arrays": "always", 21 | "functions": "never", 22 | "typeLiterals": "ignore" 23 | } 24 | } 25 | ], 26 | "no-console": false, 27 | "interface-over-type-literal": false, 28 | "no-bitwise": false, 29 | "array-type": false, 30 | "no-empty": false, 31 | "object-literal-sort-keys": false, 32 | "no-empty-interface": false, 33 | "max-classes-per-file": false, 34 | "no-unused-expression": false, 35 | "arrow-parens": false, 36 | "interface-name": false, 37 | "forin": false, 38 | "member-access": [ 39 | true, 40 | "no-public" 41 | ], 42 | "prettier": true, 43 | "ter-arrow-body-style": [ 44 | false, 45 | "never" 46 | ] 47 | }, 48 | "rulesDirectory": [ 49 | "tslint-plugin-prettier" 50 | ] 51 | } 52 | --------------------------------------------------------------------------------