├── .gitignore ├── FlexBuffers ├── BitWidth.cs ├── CsvToFlexBufferConverter.cs ├── FlexBuffer.cs ├── FlexBufferBuilder.cs ├── FlexBuffers.csproj ├── FlxValue.cs ├── JsonToFlexBufferConverter.cs ├── Properties │ └── AssemblyInfo.cs ├── StackValue.cs ├── TextScanner.cs ├── Types.cs └── XmlToFlexBufferConverter.cs ├── FlexBuffersCSharp.sln ├── FlexBuffersCSharpTests ├── BitWidthTests.cs ├── CsvToFlexBuffersConverterTests.cs ├── FlexBufferBuilderTests.cs ├── FlexBufferTests.cs ├── FlexBuffersCSharpTests.csproj ├── FlxValueTests.cs ├── JsonToFlexBufferConverterTests.cs ├── Properties │ └── AssemblyInfo.cs ├── XmlToFlexBufferConverterTests.cs └── packages.config ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | FlexBuffers/bin 2 | FlexBuffers/obj 3 | FlexBuffersCSharpTests/bin 4 | FlexBuffersCSharpTests/obj 5 | packages 6 | -------------------------------------------------------------------------------- /FlexBuffers/BitWidth.cs: -------------------------------------------------------------------------------- 1 | namespace FlexBuffers 2 | { 3 | public enum BitWidth: byte 4 | { 5 | Width8, Width16, Width32, Width64 6 | } 7 | 8 | public static class BitWidthUtil 9 | { 10 | public static BitWidth Width(sbyte value) 11 | { 12 | return BitWidth.Width8; 13 | } 14 | 15 | public static BitWidth Width(short value) 16 | { 17 | if (value >= 0) 18 | { 19 | return value <= sbyte.MaxValue ? BitWidth.Width8 : BitWidth.Width16; 20 | } 21 | return value >= sbyte.MinValue ? BitWidth.Width8 : BitWidth.Width16; 22 | } 23 | 24 | public static BitWidth Width(int value) 25 | { 26 | if (value >= 0) 27 | { 28 | if (value <= sbyte.MaxValue) 29 | { 30 | return BitWidth.Width8; 31 | } 32 | return value <= short.MaxValue ? BitWidth.Width16 : BitWidth.Width32; 33 | } 34 | if (value >= sbyte.MinValue) 35 | { 36 | return BitWidth.Width8; 37 | } 38 | return value >= short.MinValue ? BitWidth.Width16 : BitWidth.Width32; 39 | } 40 | 41 | public static BitWidth Width(long value) 42 | { 43 | if (value >= 0) 44 | { 45 | return value <= int.MaxValue ? Width((int) value) : BitWidth.Width64; 46 | } 47 | else 48 | { 49 | return value >= int.MinValue ? Width((int) value) : BitWidth.Width64; 50 | } 51 | } 52 | 53 | public static BitWidth Width(byte value) 54 | { 55 | return BitWidth.Width8; 56 | } 57 | 58 | public static BitWidth Width(ushort value) 59 | { 60 | return value <= byte.MaxValue ? BitWidth.Width8 : BitWidth.Width16; 61 | } 62 | 63 | public static BitWidth Width(uint value) 64 | { 65 | if (value <= byte.MaxValue) 66 | { 67 | return BitWidth.Width8; 68 | } 69 | 70 | return value <= ushort.MaxValue ? BitWidth.Width16 : BitWidth.Width32; 71 | } 72 | 73 | public static BitWidth Width(ulong value) 74 | { 75 | return value <= uint.MaxValue ? Width((uint) value) : BitWidth.Width64; 76 | } 77 | 78 | public static BitWidth Width(float value) 79 | { 80 | return BitWidth.Width32; 81 | } 82 | 83 | public static BitWidth Width(double value) 84 | { 85 | return ((double)((float)value)) == value ? BitWidth.Width32 : BitWidth.Width64; 86 | } 87 | 88 | public static ulong PaddingSize(ulong bufSize, ulong scalarSize) 89 | { 90 | return (~bufSize + 1) & (scalarSize - 1); 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /FlexBuffers/CsvToFlexBufferConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace FlexBuffers 4 | { 5 | public static class CsvToFlexBufferConverter 6 | { 7 | public static byte[] Convert(string csv, char separator = ',') 8 | { 9 | var flx = new FlexBuffer(); 10 | var sb = new StringBuilder(); 11 | var outerVec = flx.StartVector(); 12 | var innerVec = -1; 13 | var isInDoubleQuotes = false; 14 | for (var offset = 0; offset < csv.Length;) 15 | { 16 | if (innerVec == -1) 17 | { 18 | innerVec = flx.StartVector(); 19 | } 20 | 21 | if (csv[offset] == '"') 22 | { 23 | if (isInDoubleQuotes == false) 24 | { 25 | isInDoubleQuotes = true; 26 | offset++; 27 | } 28 | else 29 | { 30 | if (csv.Length > offset + 1 && csv[offset + 1] == '"') 31 | { 32 | sb.Append('"'); 33 | offset += 2; 34 | } 35 | else 36 | { 37 | isInDoubleQuotes = false; 38 | offset++; 39 | } 40 | } 41 | 42 | } else if (csv[offset] == separator && isInDoubleQuotes == false) 43 | { 44 | flx.Add(sb.ToString()); 45 | sb.Clear(); 46 | offset++; 47 | } else if (csv[offset] == '\n' && isInDoubleQuotes == false) 48 | { 49 | flx.Add(sb.ToString()); 50 | flx.EndVector(innerVec, false, false); 51 | innerVec = -1; 52 | sb.Clear(); 53 | offset++; 54 | } else if (csv[offset] == '\r' && csv.Length > offset + 1 && csv[offset + 1] == '\n' && isInDoubleQuotes == false) 55 | { 56 | flx.Add(sb.ToString()); 57 | flx.EndVector(innerVec, false, false); 58 | innerVec = -1; 59 | sb.Clear(); 60 | offset += 2; 61 | } 62 | else 63 | { 64 | sb.Append(csv[offset]); 65 | offset++; 66 | } 67 | } 68 | 69 | if (innerVec != -1) 70 | { 71 | flx.Add(sb.ToString()); 72 | flx.EndVector(innerVec, false, false); 73 | } 74 | flx.EndVector(outerVec, false, false); 75 | return flx.Finish(); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /FlexBuffers/FlexBuffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace FlexBuffers 7 | { 8 | public class FlexBuffer 9 | { 10 | [Flags] 11 | public enum Options: byte 12 | { 13 | None = 0, 14 | ShareKeys = 1, 15 | ShareStrings = 1 << 1, 16 | ShareKeyVectors = 1 << 2, 17 | } 18 | private readonly List _stack = new List(); 19 | private readonly Dictionary _stringCache = new Dictionary(); 20 | private readonly Dictionary _keyCache = new Dictionary(); 21 | private readonly Dictionary _keyVectorCache = new Dictionary(new OffsetArrayComparer()); 22 | private byte[] _bytes; 23 | private ulong _size = 2048; 24 | private ulong _offset; 25 | private readonly Options _options; 26 | private bool _finished = false; 27 | 28 | public FlexBuffer(ulong size = 2048, Options options = Options.ShareKeys | Options.ShareStrings | Options.ShareKeyVectors) 29 | { 30 | if (size > 0) 31 | { 32 | _size = size; 33 | } 34 | _bytes = new byte[size]; 35 | _offset = 0; 36 | _options = options; 37 | } 38 | 39 | public static byte[] Null() 40 | { 41 | var buffer = new FlexBuffer(3); 42 | buffer.AddNull(); 43 | return buffer.Finish(); 44 | } 45 | 46 | public static byte[] SingleValue(long value) 47 | { 48 | var buffer = new FlexBuffer(10); 49 | buffer.Add(value); 50 | return buffer.Finish(); 51 | } 52 | 53 | public static byte[] SingleValue(ulong value) 54 | { 55 | var buffer = new FlexBuffer(10); 56 | buffer.Add(value); 57 | return buffer.Finish(); 58 | } 59 | 60 | public static byte[] SingleValue(double value) 61 | { 62 | var buffer = new FlexBuffer(10); 63 | buffer.Add(value); 64 | return buffer.Finish(); 65 | } 66 | 67 | public static byte[] SingleValue(bool value) 68 | { 69 | var buffer = new FlexBuffer(3); 70 | buffer.Add(value); 71 | return buffer.Finish(); 72 | } 73 | 74 | public static byte[] SingleValue(string value) 75 | { 76 | var buffer = new FlexBuffer((ulong)value.Length + 2); 77 | buffer.Add(value); 78 | return buffer.Finish(); 79 | } 80 | 81 | public static byte[] SingleValue(long x, long y) 82 | { 83 | var buffer = new FlexBuffer(20); 84 | var start = buffer.StartVector(); 85 | buffer.Add(x); 86 | buffer.Add(y); 87 | buffer.EndVector(start, true, true); 88 | return buffer.Finish(); 89 | } 90 | 91 | public static byte[] SingleValue(long x, long y, long z) 92 | { 93 | var buffer = new FlexBuffer(28); 94 | var start = buffer.StartVector(); 95 | buffer.Add(x); 96 | buffer.Add(y); 97 | buffer.Add(z); 98 | buffer.EndVector(start, true, true); 99 | return buffer.Finish(); 100 | } 101 | 102 | public static byte[] SingleValue(long x, long y, long z, long w) 103 | { 104 | var buffer = new FlexBuffer(36); 105 | var start = buffer.StartVector(); 106 | buffer.Add(x); 107 | buffer.Add(y); 108 | buffer.Add(z); 109 | buffer.Add(w); 110 | buffer.EndVector(start, true, true); 111 | return buffer.Finish(); 112 | } 113 | 114 | public static byte[] SingleValue(ulong x, ulong y) 115 | { 116 | var buffer = new FlexBuffer(20); 117 | var start = buffer.StartVector(); 118 | buffer.Add(x); 119 | buffer.Add(y); 120 | buffer.EndVector(start, true, true); 121 | return buffer.Finish(); 122 | } 123 | 124 | public static byte[] SingleValue(ulong x, ulong y, ulong z) 125 | { 126 | var buffer = new FlexBuffer(28); 127 | var start = buffer.StartVector(); 128 | buffer.Add(x); 129 | buffer.Add(y); 130 | buffer.Add(z); 131 | buffer.EndVector(start, true, true); 132 | return buffer.Finish(); 133 | } 134 | 135 | public static byte[] SingleValue(ulong x, ulong y, ulong z, ulong w) 136 | { 137 | var buffer = new FlexBuffer(36); 138 | var start = buffer.StartVector(); 139 | buffer.Add(x); 140 | buffer.Add(y); 141 | buffer.Add(z); 142 | buffer.Add(w); 143 | buffer.EndVector(start, true, true); 144 | return buffer.Finish(); 145 | } 146 | 147 | public static byte[] SingleValue(double x, double y) 148 | { 149 | var buffer = new FlexBuffer(20); 150 | var start = buffer.StartVector(); 151 | buffer.Add(x); 152 | buffer.Add(y); 153 | buffer.EndVector(start, true, true); 154 | return buffer.Finish(); 155 | } 156 | 157 | public static byte[] SingleValue(double x, double y, double z) 158 | { 159 | var buffer = new FlexBuffer(28); 160 | var start = buffer.StartVector(); 161 | buffer.Add(x); 162 | buffer.Add(y); 163 | buffer.Add(z); 164 | buffer.EndVector(start, true, true); 165 | return buffer.Finish(); 166 | } 167 | 168 | public static byte[] SingleValue(double x, double y, double z, double w) 169 | { 170 | var buffer = new FlexBuffer(36); 171 | var start = buffer.StartVector(); 172 | buffer.Add(x); 173 | buffer.Add(y); 174 | buffer.Add(z); 175 | buffer.Add(w); 176 | buffer.EndVector(start, true, true); 177 | return buffer.Finish(); 178 | } 179 | 180 | public static byte[] SingleValue(byte[] blob) 181 | { 182 | var buffer = new FlexBuffer((ulong)blob.Length + 10); 183 | buffer.Add(blob); 184 | return buffer.Finish(); 185 | } 186 | 187 | public static byte[] From(IEnumerable value, Options options = Options.ShareKeys | Options.ShareStrings | Options.ShareKeyVectors) 188 | { 189 | var buffer = new FlexBuffer(options:options); 190 | if (value is IDictionary dictionary) 191 | { 192 | buffer.AddDynamicMap(dictionary); 193 | } 194 | else 195 | { 196 | buffer.AddDynamicVector(value); 197 | } 198 | 199 | return buffer.Finish(); 200 | } 201 | 202 | public byte[] Finish() 203 | { 204 | if (_finished == false) 205 | { 206 | FinishBuffer(); 207 | } 208 | var result = new byte[_offset]; 209 | Buffer.BlockCopy(_bytes, 0, result, 0, (int) _offset); 210 | return result; 211 | } 212 | 213 | private void FinishBuffer() 214 | { 215 | if (_finished) 216 | { 217 | throw new Exception("FlexBuffer is already finished"); 218 | } 219 | 220 | if (_stack.Count != 1) 221 | { 222 | throw new Exception("Stack needs to be exactly 1"); 223 | } 224 | 225 | var value = _stack[0]; 226 | 227 | var byteWidth = Align(value.ElementWidth(_offset, 0)); 228 | 229 | Write(value, byteWidth); 230 | Write(value.StoredPackedType()); 231 | Write(byteWidth); 232 | _finished = true; 233 | } 234 | 235 | internal Type AddNull() 236 | { 237 | _stack.Add(StackValue.Null()); 238 | return Type.Null; 239 | } 240 | 241 | internal Type Add(long value) 242 | { 243 | _stack.Add(StackValue.Value(value)); 244 | return Type.Int; 245 | } 246 | 247 | internal Type AddIndirect(long value) 248 | { 249 | var type = Type.IndirectInt; 250 | var bitWidth = BitWidthUtil.Width(value); 251 | var byteWidth = Align(bitWidth); 252 | var valueOffset = _offset; 253 | Write(value, byteWidth); 254 | _stack.Add(StackValue.Value(valueOffset, bitWidth, type)); 255 | return type; 256 | } 257 | 258 | internal Type Add(ulong value) 259 | { 260 | _stack.Add(StackValue.Value(value)); 261 | return Type.Uint; 262 | } 263 | 264 | internal Type AddIndirect(ulong value) 265 | { 266 | var type = Type.IndirectUInt; 267 | var bitWidth = BitWidthUtil.Width(value); 268 | var byteWidth = Align(bitWidth); 269 | var valueOffset = _offset; 270 | Write(value, byteWidth); 271 | _stack.Add(StackValue.Value(valueOffset, bitWidth, type)); 272 | return type; 273 | } 274 | 275 | internal Type Add(double value) 276 | { 277 | _stack.Add(StackValue.Value(value)); 278 | return Type.Float; 279 | } 280 | 281 | internal Type AddIndirect(double value) 282 | { 283 | var type = Type.IndirectFloat; 284 | var bitWidth = BitWidthUtil.Width(value); 285 | var byteWidth = Align(bitWidth); 286 | var valueOffset = _offset; 287 | Write(value, byteWidth); 288 | _stack.Add(StackValue.Value(valueOffset, bitWidth, type)); 289 | return type; 290 | } 291 | 292 | internal Type Add(bool value) 293 | { 294 | _stack.Add(StackValue.Value(value)); 295 | return Type.Bool; 296 | } 297 | 298 | internal Type Add(string value) 299 | { 300 | 301 | var bytes = Encoding.UTF8.GetBytes(value); 302 | var length = (ulong)bytes.Length; 303 | var bitWidth = BitWidthUtil.Width(length); 304 | if (_options.HasFlag(Options.ShareStrings) && _stringCache.ContainsKey(value)) 305 | { 306 | _stack.Add(StackValue.Value(_stringCache[value], bitWidth, Type.String)); 307 | return Type.String; 308 | } 309 | var byteWidth = Align(bitWidth); 310 | Write(length, byteWidth); 311 | var stringOffset = _offset; 312 | var newOffset = NewOffset(length + 1); 313 | Buffer.BlockCopy(bytes, 0, _bytes, (int)_offset, (int)length); 314 | _offset = newOffset; 315 | _stack.Add(StackValue.Value(stringOffset, bitWidth, Type.String)); 316 | if (_options.HasFlag(Options.ShareStrings)) 317 | { 318 | _stringCache[value] = stringOffset; 319 | } 320 | return Type.String; 321 | } 322 | 323 | internal Type Add(byte[] value) 324 | { 325 | var length = (ulong)value.Length; 326 | var bitWidth = BitWidthUtil.Width(length); 327 | var byteWidth = Align(bitWidth); 328 | Write(value.Length, byteWidth); 329 | 330 | var newOffset = NewOffset(length); 331 | var blobOffset = _offset; 332 | Buffer.BlockCopy(value, 0, _bytes, (int)_offset, value.Length); 333 | _offset = newOffset; 334 | _stack.Add(StackValue.Value(blobOffset, bitWidth, Type.Blob)); 335 | return Type.Blob; 336 | } 337 | 338 | private void AddDynamicVector(IEnumerable values) 339 | { 340 | var start = StartVector(); 341 | var typed = true; 342 | var prevType = -1; 343 | foreach (object value in values) 344 | { 345 | var currentType = AddDynamic(value); 346 | 347 | if (typed == false || TypesUtil.IsTypedVectorElement(currentType) == false) 348 | { 349 | typed = false; 350 | continue; 351 | } 352 | 353 | if (prevType == -1) 354 | { 355 | prevType = (int)currentType; 356 | } 357 | 358 | if (typed) 359 | { 360 | typed = prevType == (int)currentType; 361 | } 362 | } 363 | EndVector(start, typed, false); 364 | } 365 | 366 | private void AddDynamicMap(IDictionary values) 367 | { 368 | var start = StartVector(); 369 | var keyStrings = new List(values.Count); 370 | foreach (var key in values.Keys) 371 | { 372 | if (key is string s) 373 | { 374 | keyStrings.Add(s); 375 | } 376 | else 377 | { 378 | throw new Exception($"Key {key} is not a string."); 379 | } 380 | } 381 | 382 | foreach (var key in keyStrings) 383 | { 384 | AddKey(key); 385 | AddDynamic(values[key]); 386 | } 387 | 388 | SortAndEndMap(start); 389 | } 390 | 391 | internal void SortAndEndMap(int start) 392 | { 393 | if (((_stack.Count - start) & 1) == 1) 394 | { 395 | throw new Exception("The stack needs to hold key value pairs (even number of elements)"); 396 | } 397 | 398 | var sorted = true; 399 | for (var i = start; i < _stack.Count - 2; i+=2) 400 | { 401 | if (ShouldFlip(_stack[i], _stack[i + 2])) 402 | { 403 | sorted = false; 404 | break; 405 | } 406 | } 407 | 408 | if (sorted == false) 409 | { 410 | for (var i = start; i < _stack.Count; i += 2) 411 | { 412 | var flipIndex = i; 413 | for (var j = i + 2; j < _stack.Count; j += 2) 414 | { 415 | if (ShouldFlip(_stack[flipIndex], _stack[j])) 416 | { 417 | flipIndex = j; 418 | } 419 | } 420 | 421 | if (flipIndex != i) 422 | { 423 | var k = _stack[flipIndex]; 424 | var v = _stack[flipIndex + 1]; 425 | _stack[flipIndex] = _stack[i]; 426 | _stack[flipIndex + 1] = _stack[i + 1]; 427 | _stack[i] = k; 428 | _stack[i + 1] = v; 429 | 430 | } 431 | } 432 | } 433 | 434 | EndMap(start); 435 | } 436 | 437 | private void EndMap(int start) 438 | { 439 | var vecLen = (_stack.Count - start) / 2; 440 | StackValue keys; 441 | if (_options.HasFlag(Options.ShareKeyVectors)) 442 | { 443 | var offsets = new long[vecLen]; 444 | for (var i = start; i < _stack.Count; i += 2) 445 | { 446 | offsets[(i - start) / 2] = _stack[i].AsLong; 447 | } 448 | 449 | if (_keyVectorCache.ContainsKey(offsets)) 450 | { 451 | keys = _keyVectorCache[offsets]; 452 | } 453 | else 454 | { 455 | keys = CreateVector(start, vecLen, 2, true, false); 456 | _keyVectorCache[offsets] = keys; 457 | } 458 | } 459 | else 460 | { 461 | keys = CreateVector(start, vecLen, 2, true, false); 462 | } 463 | 464 | var vec = CreateVector(start + 1, vecLen, 2, false, false, keys); 465 | _stack.RemoveRange(_stack.Count - vecLen * 2, vecLen * 2); 466 | _stack.Add(vec); 467 | } 468 | 469 | private bool ShouldFlip(StackValue v1, StackValue v2) 470 | { 471 | if (v1.TypeOfValue != Type.Key || v2.TypeOfValue != Type.Key) 472 | { 473 | throw new Exception($"Stack values are not keys {v1} | {v2}"); 474 | } 475 | 476 | byte c1, c2; 477 | var index = 0; 478 | do 479 | { 480 | c1 = _bytes[v1.AsLong + index]; 481 | c2 = _bytes[v2.AsLong + index]; 482 | if (c2 < c1) 483 | { 484 | return true; 485 | } 486 | 487 | if (c1 < c2) 488 | { 489 | return false; 490 | } 491 | 492 | index++; 493 | } while (c1 != 0 && c2 != 0); 494 | 495 | return false; 496 | } 497 | 498 | private Type AddDynamic(object value) 499 | { 500 | switch (value) 501 | { 502 | case null: 503 | return AddNull(); 504 | case string s1: 505 | return Add(s1); 506 | case bool b1: 507 | return Add(b1); 508 | case sbyte i1: 509 | return Add(i1); 510 | case short i1: 511 | return Add(i1); 512 | case int i1: 513 | return Add(i1); 514 | case long i1: 515 | return Add(i1); 516 | case byte l1: 517 | return Add(l1); 518 | case ushort l1: 519 | return Add(l1); 520 | case uint l1: 521 | return Add(l1); 522 | case ulong l1: 523 | return Add(l1); 524 | case double d1: 525 | return Add(d1); 526 | case IDictionary d: 527 | AddDynamicMap(d); 528 | return Type.Map; 529 | case IEnumerable l: 530 | AddDynamicVector(l); 531 | return Type.Vector; 532 | default: 533 | throw new Exception($"Unexpected type of {value}"); 534 | } 535 | } 536 | 537 | internal void AddKey(string value) 538 | { 539 | if (_options.HasFlag(Options.ShareKeys) && _keyCache.ContainsKey(value)) 540 | { 541 | _stack.Add(StackValue.Value(_keyCache[value], BitWidth.Width8, Type.Key)); 542 | return; 543 | } 544 | var bytes = Encoding.UTF8.GetBytes(value); 545 | var length = (ulong)bytes.Length; 546 | var keyOffset = _offset; 547 | var newOffset = NewOffset(length + 1); 548 | Buffer.BlockCopy(bytes, 0, _bytes, (int)_offset, (int)length); 549 | _offset = newOffset; 550 | _stack.Add(StackValue.Value(keyOffset, BitWidth.Width8, Type.Key)); 551 | if (_options.HasFlag(Options.ShareKeys)) 552 | { 553 | _keyCache[value] = keyOffset; 554 | } 555 | } 556 | 557 | private byte Align(BitWidth width) 558 | { 559 | var byteWidth = 1UL << (int) width; 560 | _offset += BitWidthUtil.PaddingSize(_offset, byteWidth); 561 | return (byte) byteWidth; 562 | } 563 | 564 | private void Write(StackValue value, ulong width) 565 | { 566 | var newOffset = NewOffset(width); 567 | if (value.IsOffset) 568 | { 569 | var relOffset = _offset - value.AsULong; 570 | if (width == 8 || relOffset < (ulong)1 << ((int)width * 8)) 571 | { 572 | Write(relOffset, width); 573 | } 574 | else 575 | { 576 | throw new Exception("Unexpected size"); 577 | } 578 | } 579 | else 580 | { 581 | var bytes = value.IsFloat32 && width == 4 ? BitConverter.GetBytes((float)value.AsDouble) : BitConverter.GetBytes(value.AsULong); 582 | var count = Math.Min((ulong)bytes.Length, width); 583 | Buffer.BlockCopy(bytes, 0, _bytes, (int)_offset, (int)count); 584 | } 585 | _offset = newOffset; 586 | } 587 | 588 | private void Write(byte value) 589 | { 590 | var newOffset = NewOffset(1); 591 | _bytes[_offset] = value; 592 | _offset = newOffset; 593 | } 594 | 595 | private void Write(long value, ulong width) 596 | { 597 | var newOffset = NewOffset(width); 598 | var bytes = BitConverter.GetBytes(value); 599 | var count = Math.Min((ulong)bytes.Length, width); 600 | Buffer.BlockCopy(bytes, 0, _bytes, (int)_offset, (int)count); 601 | _offset = newOffset; 602 | } 603 | 604 | private void Write(ulong value, ulong width) 605 | { 606 | var newOffset = NewOffset(width); 607 | var bytes = BitConverter.GetBytes(value); 608 | var count = Math.Min((ulong)bytes.Length, width); 609 | Buffer.BlockCopy(bytes, 0, _bytes, (int)_offset, (int)count); 610 | _offset = newOffset; 611 | } 612 | 613 | private void Write(double value, ulong width) 614 | { 615 | var newOffset = NewOffset(width); 616 | var bytes = BitConverter.GetBytes(value); 617 | var count = Math.Min((ulong)bytes.Length, width); 618 | Buffer.BlockCopy(bytes, 0, _bytes, (int)_offset, (int)count); 619 | _offset = newOffset; 620 | } 621 | 622 | private ulong NewOffset(ulong width) 623 | { 624 | var newOffset = _offset + width; 625 | var prevSize = _size; 626 | while (_size < newOffset) 627 | { 628 | _size <<= 1; 629 | } 630 | 631 | if (prevSize < _size) 632 | { 633 | var prevBytes = _bytes; 634 | _bytes = new byte[_size]; 635 | Buffer.BlockCopy(prevBytes, 0, _bytes, 0, (int)_offset); 636 | } 637 | 638 | return newOffset; 639 | } 640 | 641 | internal int StartVector() 642 | { 643 | return _stack.Count; 644 | } 645 | 646 | internal int EndVector(int start, bool typed, bool fix) 647 | { 648 | var vecLen = _stack.Count - start; 649 | var vec = CreateVector(start, vecLen, 1, typed, fix); 650 | 651 | _stack.RemoveRange(_stack.Count - vecLen, vecLen); 652 | _stack.Add(vec); 653 | return (int)vec.AsLong; 654 | } 655 | 656 | private StackValue CreateVector(int start, int vecLen, int step, bool typed, bool fix, StackValue? keys = null) 657 | { 658 | var bitWidth = BitWidthUtil.Width(vecLen); 659 | var prefixElems = 1; 660 | if (keys != null) 661 | { 662 | var elemWidth = keys.Value.ElementWidth(_offset, 0); 663 | if ((int) elemWidth > (int) bitWidth) 664 | { 665 | bitWidth = elemWidth; 666 | } 667 | 668 | prefixElems += 2; 669 | } 670 | 671 | var vectorType = Type.Key; 672 | for (var i = start; i < _stack.Count; i+=step) 673 | { 674 | var elemWidth = _stack[i].ElementWidth(_offset, i + prefixElems); 675 | if ((int) elemWidth > (int) bitWidth) 676 | { 677 | bitWidth = elemWidth; 678 | } 679 | 680 | if (typed) 681 | { 682 | if (i == start) 683 | { 684 | vectorType = _stack[i].TypeOfValue; 685 | } 686 | else 687 | { 688 | if (vectorType != _stack[i].TypeOfValue) 689 | { 690 | throw new Exception($"Your typed vector is of type {vectorType} but the item on index {i} is of type {_stack[i].TypeOfValue}"); 691 | } 692 | } 693 | } 694 | } 695 | 696 | if (TypesUtil.IsTypedVectorElement(vectorType) == false) 697 | { 698 | throw new Exception("Your fixed types are not one of: Int / UInt / Float / Key"); 699 | } 700 | 701 | var byteWidth = Align(bitWidth); 702 | if (keys != null) 703 | { 704 | Write(keys.Value, byteWidth); 705 | Write(1 << (int)keys.Value.InternalWidth, byteWidth); 706 | } 707 | 708 | if (!fix) 709 | { 710 | Write(vecLen, byteWidth); 711 | } 712 | 713 | var vloc = _offset; 714 | 715 | for (var i = start; i < _stack.Count; i += step) 716 | { 717 | Write(_stack[i], byteWidth); 718 | } 719 | 720 | if (!typed) 721 | { 722 | for (var i = start; i < _stack.Count; i += step) 723 | { 724 | Write(_stack[i].StoredPackedType()); 725 | } 726 | } 727 | 728 | 729 | if (keys != null) 730 | { 731 | return StackValue.Value(vloc, bitWidth, Type.Map); 732 | } 733 | 734 | if (typed) 735 | { 736 | var type = TypesUtil.ToTypedVector(vectorType, (byte)(fix ? vecLen : 0)); 737 | return StackValue.Value(vloc, bitWidth, type); 738 | } 739 | 740 | return StackValue.Value(vloc, bitWidth, Type.Vector); 741 | } 742 | } 743 | 744 | internal class OffsetArrayComparer : IEqualityComparer 745 | { 746 | public bool Equals(long[] x, long[] y) 747 | { 748 | if (x.Length != y.Length) 749 | { 750 | return false; 751 | } 752 | for (var i = 0; i < x.Length; i++) 753 | { 754 | if (x[i] != y[i]) 755 | { 756 | return false; 757 | } 758 | } 759 | return true; 760 | } 761 | 762 | public int GetHashCode(long[] obj) 763 | { 764 | var result = 17; 765 | for (var i = 0; i < obj.Length; i++) 766 | { 767 | unchecked 768 | { 769 | result = (int) (result * 23 + obj[i]); 770 | } 771 | } 772 | return result; 773 | } 774 | } 775 | } -------------------------------------------------------------------------------- /FlexBuffers/FlexBufferBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace FlexBuffers 5 | { 6 | public class FlexBufferBuilder 7 | { 8 | public static byte[] Map(Action map) 9 | { 10 | var buffer = new FlexBuffer(); 11 | var start = buffer.StartVector(); 12 | var builder = new FlexBufferMapBuilder(buffer); 13 | map(builder); 14 | buffer.SortAndEndMap(start); 15 | return buffer.Finish(); 16 | } 17 | 18 | public static byte[] Vector(Action vector) 19 | { 20 | var buffer = new FlexBuffer(); 21 | var start = buffer.StartVector(); 22 | var builder = new FlexBufferVectorBuilder(buffer); 23 | vector(builder); 24 | buffer.EndVector(start, false, false); 25 | return buffer.Finish(); 26 | } 27 | } 28 | 29 | public interface IFlexBufferMapBuilder 30 | { 31 | void AddNull(string key); 32 | void Add(string key, long value, bool indirect = false); 33 | void Add(string key, long x, long y); 34 | void Add(string key, long x, long y, long z); 35 | void Add(string key, long x, long y, long z, long w); 36 | void Add(string key, ulong value, bool indirect = false); 37 | void Add(string key, ulong x, ulong y); 38 | void Add(string key, ulong x, ulong y, ulong z); 39 | void Add(string key, ulong x, ulong y, ulong z, ulong w); 40 | void Add(string key, double value, bool indirect = false); 41 | void Add(string key, double x, double y); 42 | void Add(string key, double x, double y, double z); 43 | void Add(string key, double x, double y, double z, double w); 44 | void Add(string key, bool value); 45 | void Add(string key, string value); 46 | void Add(string key, byte[] value); 47 | void Map(string key, Action map); 48 | void Vector(string key, Action vector); 49 | } 50 | 51 | public interface IFlexBufferVectorBuilder 52 | { 53 | void AddNull(); 54 | void Add(long value, bool indirect = false); 55 | void Add(long x, long y); 56 | void Add(long x, long y, long z); 57 | void Add(long x, long y, long z, long w); 58 | void Add(ulong value, bool indirect = false); 59 | void Add(ulong x, ulong y); 60 | void Add(ulong x, ulong y, ulong z); 61 | void Add(ulong x, ulong y, ulong z, ulong w); 62 | void Add(double value, bool indirect = false); 63 | void Add(double x, double y); 64 | void Add(double x, double y, double z); 65 | void Add(double x, double y, double z, double w); 66 | void Add(bool value); 67 | void Add(string value); 68 | void Add(byte[] value); 69 | void Map(Action map); 70 | void Vector(Action vector); 71 | } 72 | 73 | internal struct FlexBufferMapBuilder : IFlexBufferMapBuilder 74 | { 75 | private readonly FlexBuffer _buffer; 76 | 77 | internal FlexBufferMapBuilder(FlexBuffer buffer) 78 | { 79 | _buffer = buffer; 80 | } 81 | 82 | public void AddNull(string key) 83 | { 84 | _buffer.AddKey(key); 85 | _buffer.AddNull(); 86 | } 87 | 88 | public void Add(string key, long value, bool indirect = false) 89 | { 90 | _buffer.AddKey(key); 91 | if (indirect) 92 | { 93 | _buffer.AddIndirect(value); 94 | } 95 | else 96 | { 97 | _buffer.Add(value); 98 | } 99 | } 100 | 101 | public void Add(string key, long x, long y) 102 | { 103 | _buffer.AddKey(key); 104 | var start = _buffer.StartVector(); 105 | _buffer.Add(x); 106 | _buffer.Add(y); 107 | _buffer.EndVector(start, true, true); 108 | } 109 | 110 | public void Add(string key, long x, long y, long z) 111 | { 112 | _buffer.AddKey(key); 113 | var start = _buffer.StartVector(); 114 | _buffer.Add(x); 115 | _buffer.Add(y); 116 | _buffer.Add(z); 117 | _buffer.EndVector(start, true, true); 118 | } 119 | 120 | public void Add(string key, long x, long y, long z, long w) 121 | { 122 | _buffer.AddKey(key); 123 | var start = _buffer.StartVector(); 124 | _buffer.Add(x); 125 | _buffer.Add(y); 126 | _buffer.Add(z); 127 | _buffer.Add(w); 128 | _buffer.EndVector(start, true, true); 129 | } 130 | 131 | public void Add(string key, ulong value, bool indirect = false) 132 | { 133 | _buffer.AddKey(key); 134 | if (indirect) 135 | { 136 | _buffer.AddIndirect(value); 137 | } 138 | else 139 | { 140 | _buffer.Add(value); 141 | } 142 | } 143 | 144 | public void Add(string key, ulong x, ulong y) 145 | { 146 | _buffer.AddKey(key); 147 | var start = _buffer.StartVector(); 148 | _buffer.Add(x); 149 | _buffer.Add(y); 150 | _buffer.EndVector(start, true, true); 151 | } 152 | 153 | public void Add(string key, ulong x, ulong y, ulong z) 154 | { 155 | _buffer.AddKey(key); 156 | var start = _buffer.StartVector(); 157 | _buffer.Add(x); 158 | _buffer.Add(y); 159 | _buffer.Add(z); 160 | _buffer.EndVector(start, true, true); 161 | } 162 | 163 | public void Add(string key, ulong x, ulong y, ulong z, ulong w) 164 | { 165 | _buffer.AddKey(key); 166 | var start = _buffer.StartVector(); 167 | _buffer.Add(x); 168 | _buffer.Add(y); 169 | _buffer.Add(z); 170 | _buffer.Add(w); 171 | _buffer.EndVector(start, true, true); 172 | } 173 | 174 | public void Add(string key, double value, bool indirect = false) 175 | { 176 | _buffer.AddKey(key); 177 | if (indirect) 178 | { 179 | _buffer.AddIndirect(value); 180 | } 181 | else 182 | { 183 | _buffer.Add(value); 184 | } 185 | } 186 | 187 | public void Add(string key, double x, double y) 188 | { 189 | _buffer.AddKey(key); 190 | var start = _buffer.StartVector(); 191 | _buffer.Add(x); 192 | _buffer.Add(y); 193 | _buffer.EndVector(start, true, true); 194 | } 195 | 196 | public void Add(string key, double x, double y, double z) 197 | { 198 | _buffer.AddKey(key); 199 | var start = _buffer.StartVector(); 200 | _buffer.Add(x); 201 | _buffer.Add(y); 202 | _buffer.Add(z); 203 | _buffer.EndVector(start, true, true); 204 | } 205 | 206 | public void Add(string key, double x, double y, double z, double w) 207 | { 208 | _buffer.AddKey(key); 209 | var start = _buffer.StartVector(); 210 | _buffer.Add(x); 211 | _buffer.Add(y); 212 | _buffer.Add(z); 213 | _buffer.Add(w); 214 | _buffer.EndVector(start, true, true); 215 | } 216 | 217 | public void Add(string key, bool value) 218 | { 219 | _buffer.AddKey(key); 220 | _buffer.Add(value); 221 | } 222 | 223 | public void Add(string key, string value) 224 | { 225 | _buffer.AddKey(key); 226 | _buffer.Add(value); 227 | } 228 | 229 | public void Add(string key, byte[] value) 230 | { 231 | _buffer.AddKey(key); 232 | _buffer.Add(value); 233 | } 234 | 235 | public void Map(string key, Action map) 236 | { 237 | _buffer.AddKey(key); 238 | var start = _buffer.StartVector(); 239 | var builder = new FlexBufferMapBuilder(_buffer); 240 | map(builder); 241 | _buffer.SortAndEndMap(start); 242 | } 243 | 244 | public void Vector(string key, Action vector) 245 | { 246 | _buffer.AddKey(key); 247 | var start = _buffer.StartVector(); 248 | var builder = new FlexBufferVectorBuilder(_buffer); 249 | vector(builder); 250 | _buffer.EndVector(start, false, false); 251 | } 252 | } 253 | 254 | internal struct FlexBufferVectorBuilder : IFlexBufferVectorBuilder 255 | { 256 | private readonly FlexBuffer _buffer; 257 | 258 | internal FlexBufferVectorBuilder(FlexBuffer buffer) 259 | { 260 | _buffer = buffer; 261 | } 262 | 263 | public void AddNull() 264 | { 265 | _buffer.AddNull(); 266 | } 267 | public void Add(long value, bool indirect = false) 268 | { 269 | if (indirect) 270 | { 271 | _buffer.AddIndirect(value); 272 | } 273 | else 274 | { 275 | _buffer.Add(value); 276 | } 277 | } 278 | 279 | public void Add(long x, long y) 280 | { 281 | var start = _buffer.StartVector(); 282 | _buffer.Add(x); 283 | _buffer.Add(y); 284 | _buffer.EndVector(start, true, true); 285 | } 286 | 287 | public void Add(long x, long y, long z) 288 | { 289 | var start = _buffer.StartVector(); 290 | _buffer.Add(x); 291 | _buffer.Add(y); 292 | _buffer.Add(z); 293 | _buffer.EndVector(start, true, true); 294 | } 295 | 296 | public void Add(long x, long y, long z, long w) 297 | { 298 | var start = _buffer.StartVector(); 299 | _buffer.Add(x); 300 | _buffer.Add(y); 301 | _buffer.Add(z); 302 | _buffer.Add(w); 303 | _buffer.EndVector(start, true, true); 304 | } 305 | 306 | public void Add(ulong value, bool indirect = false) 307 | { 308 | if (indirect) 309 | { 310 | _buffer.AddIndirect(value); 311 | } 312 | else 313 | { 314 | _buffer.Add(value); 315 | } 316 | } 317 | 318 | public void Add(ulong x, ulong y) 319 | { 320 | var start = _buffer.StartVector(); 321 | _buffer.Add(x); 322 | _buffer.Add(y); 323 | _buffer.EndVector(start, true, true); 324 | } 325 | 326 | public void Add(ulong x, ulong y, ulong z) 327 | { 328 | var start = _buffer.StartVector(); 329 | _buffer.Add(x); 330 | _buffer.Add(y); 331 | _buffer.Add(z); 332 | _buffer.EndVector(start, true, true); 333 | } 334 | 335 | public void Add(ulong x, ulong y, ulong z, ulong w) 336 | { 337 | var start = _buffer.StartVector(); 338 | _buffer.Add(x); 339 | _buffer.Add(y); 340 | _buffer.Add(z); 341 | _buffer.Add(w); 342 | _buffer.EndVector(start, true, true); 343 | } 344 | 345 | public void Add(double value, bool indirect = false) 346 | { 347 | if (indirect) 348 | { 349 | _buffer.AddIndirect(value); 350 | } 351 | else 352 | { 353 | _buffer.Add(value); 354 | } 355 | } 356 | 357 | public void Add(double x, double y) 358 | { 359 | var start = _buffer.StartVector(); 360 | _buffer.Add(x); 361 | _buffer.Add(y); 362 | _buffer.EndVector(start, true, true); 363 | } 364 | 365 | public void Add(double x, double y, double z) 366 | { 367 | var start = _buffer.StartVector(); 368 | _buffer.Add(x); 369 | _buffer.Add(y); 370 | _buffer.Add(z); 371 | _buffer.EndVector(start, true, true); 372 | } 373 | 374 | public void Add(double x, double y, double z, double w) 375 | { 376 | var start = _buffer.StartVector(); 377 | _buffer.Add(x); 378 | _buffer.Add(y); 379 | _buffer.Add(z); 380 | _buffer.Add(w); 381 | _buffer.EndVector(start, true, true); 382 | } 383 | 384 | public void Add(bool value) 385 | { 386 | _buffer.Add(value); 387 | } 388 | 389 | public void Add(string value) 390 | { 391 | _buffer.Add(value); 392 | } 393 | 394 | public void Add(byte[] value) 395 | { 396 | _buffer.Add(value); 397 | } 398 | 399 | public void Map(Action map) 400 | { 401 | var start = _buffer.StartVector(); 402 | var builder = new FlexBufferMapBuilder(_buffer); 403 | map(builder); 404 | _buffer.SortAndEndMap(start); 405 | } 406 | 407 | public void Vector(Action vector) 408 | { 409 | var start = _buffer.StartVector(); 410 | var builder = new FlexBufferVectorBuilder(_buffer); 411 | vector(builder); 412 | _buffer.EndVector(start, false, false); 413 | } 414 | } 415 | } -------------------------------------------------------------------------------- /FlexBuffers/FlexBuffers.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {413DE2E2-0412-4EEE-AB96-BB4EF5EECAE1} 8 | Library 9 | Properties 10 | FlexBuffersCSharp 11 | FlexBuffers 12 | v4.7.1 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 62 | -------------------------------------------------------------------------------- /FlexBuffers/FlxValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | using System.Text; 6 | 7 | namespace FlexBuffers 8 | { 9 | public struct FlxValue 10 | { 11 | private readonly byte[] _buffer; 12 | private readonly int _offset; 13 | private readonly byte _parentWidth; 14 | private readonly byte _byteWidth; 15 | private readonly Type _type; 16 | 17 | internal FlxValue(byte[] buffer, int offset, byte parentWidth, byte packedType) 18 | { 19 | _buffer = buffer; 20 | _offset = offset; 21 | _parentWidth = parentWidth; 22 | _byteWidth = (byte) (1 << (packedType & 3)); 23 | _type = (Type) (packedType >> 2); 24 | } 25 | 26 | internal FlxValue(byte[] buffer, int offset, byte parentWidth, byte byteWidth, Type type) 27 | { 28 | _buffer = buffer; 29 | _offset = offset; 30 | _parentWidth = parentWidth; 31 | _byteWidth = byteWidth; 32 | _type = type; 33 | } 34 | 35 | public static FlxValue FromBytes(byte[] bytes) 36 | { 37 | if (bytes.Length < 3) 38 | { 39 | throw new Exception($"Invalid buffer {bytes}"); 40 | } 41 | 42 | var byteWidth = bytes[bytes.Length - 1]; 43 | var packedType = bytes[bytes.Length - 2]; 44 | var offset = bytes.Length - byteWidth - 2; 45 | return new FlxValue(bytes, offset, byteWidth, packedType); 46 | } 47 | 48 | public Type ValueType => _type; 49 | public int BufferOffset => _offset; 50 | 51 | public bool IsNull => _type == Type.Null; 52 | 53 | public long AsLong 54 | { 55 | get 56 | { 57 | if (_type == Type.Int) 58 | { 59 | return ReadLong(_buffer, _offset, _parentWidth); 60 | } 61 | 62 | if (_type == Type.IndirectInt) 63 | { 64 | var indirectOffset = ComputeIndirectOffset(_buffer, _offset, _parentWidth); 65 | return ReadLong(_buffer, indirectOffset, _byteWidth); 66 | } 67 | 68 | if (_type == Type.Uint) 69 | { 70 | var value = ReadULong(_buffer, _offset, _parentWidth); 71 | if (value <= long.MaxValue) 72 | { 73 | return (long) value; 74 | } 75 | } 76 | if (_type == Type.IndirectUInt) 77 | { 78 | var indirectOffset = ComputeIndirectOffset(_buffer, _offset, _parentWidth); 79 | var value = ReadULong(_buffer, indirectOffset, _byteWidth); 80 | if (value <= long.MaxValue) 81 | { 82 | return (long) value; 83 | } 84 | } 85 | throw new Exception($"Type {_type} is not convertible to long"); 86 | } 87 | } 88 | 89 | public ulong AsULong 90 | { 91 | get 92 | { 93 | if (_type == Type.Uint) 94 | { 95 | return ReadULong(_buffer, _offset, _parentWidth); 96 | } 97 | 98 | if (_type == Type.IndirectUInt) 99 | { 100 | var indirectOffset = ComputeIndirectOffset(_buffer, _offset, _parentWidth); 101 | return ReadULong(_buffer, indirectOffset, _byteWidth); 102 | } 103 | 104 | if (_type == Type.Int) 105 | { 106 | var value = ReadLong(_buffer, _offset, _parentWidth); 107 | if (value >= 0) 108 | { 109 | return (ulong) value; 110 | } 111 | } 112 | 113 | if (_type == Type.IndirectInt) 114 | { 115 | var indirectOffset = ComputeIndirectOffset(_buffer, _offset, _parentWidth); 116 | var value = ReadLong(_buffer, indirectOffset, _byteWidth); 117 | if (value >= 0) 118 | { 119 | return (ulong) value; 120 | } 121 | } 122 | throw new Exception($"Type {_type} is not convertible to ulong"); 123 | } 124 | } 125 | 126 | public double AsDouble 127 | { 128 | get 129 | { 130 | if (_type == Type.Float) 131 | { 132 | return ReadDouble(_buffer, _offset, _parentWidth); 133 | } 134 | if (_type == Type.Int) 135 | { 136 | return ReadLong(_buffer, _offset, _parentWidth); 137 | } 138 | if (_type == Type.Uint) 139 | { 140 | return ReadULong(_buffer, _offset, _parentWidth); 141 | } 142 | if (_type == Type.IndirectFloat) 143 | { 144 | var indirectOffset = ComputeIndirectOffset(_buffer, _offset, _parentWidth); 145 | return ReadDouble(_buffer, indirectOffset, _byteWidth); 146 | } 147 | if (_type == Type.IndirectUInt) 148 | { 149 | var indirectOffset = ComputeIndirectOffset(_buffer, _offset, _parentWidth); 150 | return ReadULong(_buffer, indirectOffset, _byteWidth); 151 | } 152 | if (_type == Type.IndirectInt) 153 | { 154 | var indirectOffset = ComputeIndirectOffset(_buffer, _offset, _parentWidth); 155 | return ReadLong(_buffer, indirectOffset, _byteWidth); 156 | } 157 | throw new Exception($"Type {_type} is not convertible to double"); 158 | } 159 | } 160 | 161 | public bool AsBool 162 | { 163 | get 164 | { 165 | if (_type == Type.Bool) 166 | { 167 | return _buffer[_offset] != 0; 168 | } 169 | if (_type == Type.Int) 170 | { 171 | return ReadLong(_buffer, _offset, _parentWidth) != 0; 172 | } 173 | if (_type == Type.Uint) 174 | { 175 | return ReadULong(_buffer, _offset, _parentWidth) != 0; 176 | } 177 | throw new Exception($"Type {_type} is not convertible to bool"); 178 | } 179 | } 180 | 181 | public string AsString 182 | { 183 | get 184 | { 185 | if (_type == Type.String) 186 | { 187 | var indirectOffset = ComputeIndirectOffset(_buffer, _offset, _parentWidth); 188 | var size = (int)ReadULong(_buffer, indirectOffset - _byteWidth, _byteWidth); 189 | var sizeWidth = (int)_byteWidth; 190 | while (_buffer[indirectOffset + size] != 0) 191 | { 192 | sizeWidth <<= 1; 193 | size = (int)ReadULong(_buffer, indirectOffset - sizeWidth, (byte)sizeWidth); 194 | } 195 | 196 | return Encoding.UTF8.GetString(_buffer, indirectOffset, size); 197 | } 198 | 199 | if (_type == Type.Key) 200 | { 201 | var indirectOffset = ComputeIndirectOffset(_buffer, _offset, _parentWidth); 202 | var size = 0; 203 | while (indirectOffset + size < _buffer.Length && _buffer[indirectOffset + size] != 0) 204 | { 205 | size++; 206 | } 207 | return Encoding.UTF8.GetString(_buffer, indirectOffset, size); 208 | } 209 | 210 | throw new Exception($"Type {_type} is not convertible to string"); 211 | } 212 | } 213 | 214 | public FlxValue this[int index] => AsVector[index]; 215 | 216 | public FlxValue this[string key] => AsMap[key]; 217 | 218 | public FlxVector AsVector 219 | { 220 | get 221 | { 222 | if (TypesUtil.IsAVector(_type) == false) 223 | { 224 | throw new Exception($"Type {_type} is not a vector."); 225 | } 226 | 227 | var indirectOffset = ComputeIndirectOffset(_buffer, _offset, _parentWidth); 228 | var size = TypesUtil.IsFixedTypedVector(_type) 229 | ? TypesUtil.FixedTypedVectorElementSize(_type) 230 | : (int)ReadULong(_buffer, indirectOffset - _byteWidth, _byteWidth); 231 | return new FlxVector(_buffer, indirectOffset, _byteWidth, _type, size); 232 | } 233 | } 234 | 235 | public FlxMap AsMap 236 | { 237 | get 238 | { 239 | if (_type != Type.Map) 240 | { 241 | throw new Exception($"Type {_type} is not a map."); 242 | } 243 | 244 | var indirectOffset = ComputeIndirectOffset(_buffer, _offset, _parentWidth); 245 | var size = ReadULong(_buffer, indirectOffset - _byteWidth, _byteWidth); 246 | return new FlxMap(_buffer, indirectOffset, _byteWidth, (int)size); 247 | } 248 | } 249 | 250 | public byte[] AsBlob 251 | { 252 | get 253 | { 254 | if (_type != Type.Blob) 255 | { 256 | throw new Exception($"Type {_type} is not a blob."); 257 | } 258 | var indirectOffset = ComputeIndirectOffset(_buffer, _offset, _parentWidth); 259 | var size = ReadULong(_buffer, indirectOffset - _byteWidth, _byteWidth); 260 | var blob = new byte[size]; 261 | System.Buffer.BlockCopy(_buffer, indirectOffset, blob, 0, (int)size); 262 | return blob; 263 | } 264 | } 265 | 266 | public string ToJson 267 | { 268 | get 269 | { 270 | if (IsNull) 271 | { 272 | return "null"; 273 | } 274 | 275 | if (_type == Type.Bool) 276 | { 277 | return AsBool ? "true" : "false"; 278 | } 279 | 280 | if (_type == Type.Int || _type == Type.IndirectInt) 281 | { 282 | return AsLong.ToString(); 283 | } 284 | 285 | if (_type == Type.Uint || _type == Type.IndirectUInt) 286 | { 287 | return AsULong.ToString(); 288 | } 289 | 290 | if (_type == Type.Float || _type == Type.IndirectFloat) 291 | { 292 | return AsDouble.ToString(CultureInfo.CurrentCulture); 293 | } 294 | 295 | if (TypesUtil.IsAVector(_type)) 296 | { 297 | return AsVector.ToJson; 298 | } 299 | 300 | if (_type == Type.String || _type == Type.Key) 301 | { 302 | var jsonConformString = AsString.Replace("\"", "\\\"") 303 | .Replace("\n", "\\n") 304 | .Replace("\r", "\\r") 305 | .Replace("\t", "\\t") 306 | .Replace("/", "\\/"); 307 | return $"\"{jsonConformString}\""; 308 | } 309 | 310 | if (_type == Type.Map) 311 | { 312 | return AsMap.ToJson; 313 | } 314 | 315 | if (_type == Type.Blob) 316 | { 317 | return $"\"{Convert.ToBase64String(AsBlob)}\""; 318 | } 319 | 320 | throw new Exception($"Unexpected type {_type}"); 321 | } 322 | } 323 | 324 | public string ToPrettyJson(string left = "", bool childrenOnly = false) 325 | { 326 | if (_type == Type.Map) 327 | { 328 | return AsMap.ToPrettyJson(left, childrenOnly); 329 | } 330 | if (TypesUtil.IsAVector(_type)) 331 | { 332 | return AsVector.ToPrettyJson(left, childrenOnly); 333 | } 334 | 335 | if (childrenOnly) 336 | { 337 | return ToJson; 338 | } 339 | 340 | return $"{left}{ToJson}"; 341 | } 342 | 343 | internal static long ReadLong(byte[] bytes, int offset, byte width) 344 | { 345 | if (offset < 0 || bytes.Length <= (offset + width) || (offset & (width - 1)) != 0) 346 | { 347 | throw new Exception("Bad offset"); 348 | } 349 | 350 | if (width == 1) 351 | { 352 | return (sbyte)bytes[offset]; 353 | } 354 | 355 | if (width == 2) 356 | { 357 | return BitConverter.ToInt16(bytes, offset); 358 | } 359 | 360 | if (width == 4) 361 | { 362 | return BitConverter.ToInt32(bytes, offset); 363 | } 364 | 365 | return BitConverter.ToInt64(bytes, offset); 366 | } 367 | 368 | internal static ulong ReadULong(byte[] bytes, int offset, byte width) 369 | { 370 | if (offset < 0 || bytes.Length <= (offset + width) || (offset & (width - 1)) != 0) 371 | { 372 | throw new Exception("Bad offset"); 373 | } 374 | 375 | if (width == 1) 376 | { 377 | return bytes[offset]; 378 | } 379 | 380 | if (width == 2) 381 | { 382 | return BitConverter.ToUInt16(bytes, offset); 383 | } 384 | 385 | if (width == 4) 386 | { 387 | return BitConverter.ToUInt32(bytes, offset); 388 | } 389 | 390 | return BitConverter.ToUInt64(bytes, offset); 391 | } 392 | 393 | internal static double ReadDouble(byte[] bytes, int offset, byte width) 394 | { 395 | if (offset < 0 || bytes.Length <= (offset + width) || (offset & (width - 1)) != 0) 396 | { 397 | throw new Exception("Bad offset"); 398 | } 399 | 400 | if (width != 4 && width != 8) 401 | { 402 | throw new Exception($"Bad width {width}"); 403 | } 404 | 405 | if (width == 4) 406 | { 407 | return BitConverter.ToSingle(bytes, offset); 408 | } 409 | 410 | return BitConverter.ToDouble(bytes, offset); 411 | } 412 | 413 | internal static int ComputeIndirectOffset(byte[] bytes, int offset, byte width) 414 | { 415 | var step = (int)ReadULong(bytes, offset, width); 416 | return offset - step; 417 | } 418 | 419 | internal byte[] Buffer => _buffer; 420 | internal int Offset => _offset; 421 | 422 | internal int IndirectOffset => ComputeIndirectOffset(_buffer, _offset, _parentWidth); 423 | 424 | } 425 | 426 | public struct FlxVector: IEnumerable 427 | { 428 | private readonly byte[] _buffer; 429 | private readonly int _offset; 430 | private readonly int _length; 431 | private readonly byte _byteWidth; 432 | private readonly Type _type; 433 | 434 | internal FlxVector(byte[] buffer, int offset, byte byteWidth, Type type, int length) 435 | { 436 | _buffer = buffer; 437 | _offset = offset; 438 | _byteWidth = byteWidth; 439 | _type = type; 440 | _length = length; 441 | } 442 | 443 | public int Length => _length; 444 | 445 | public FlxValue this[int index] 446 | { 447 | get 448 | { 449 | if (index < 0 || index >= _length) 450 | { 451 | throw new Exception($"Bad index {index}, should be 0...{_length}"); 452 | } 453 | 454 | if (TypesUtil.IsTypedVector(_type)) 455 | { 456 | var elemOffset = _offset + (index * _byteWidth); 457 | return new FlxValue(_buffer, elemOffset, _byteWidth, 1, TypesUtil.TypedVectorElementType(_type)); 458 | } 459 | 460 | if (TypesUtil.IsFixedTypedVector(_type)) 461 | { 462 | var elemOffset = _offset + (index * _byteWidth); 463 | return new FlxValue(_buffer, elemOffset, _byteWidth, 1, TypesUtil.FixedTypedVectorElementType(_type)); 464 | } 465 | 466 | if (_type == Type.Vector) 467 | { 468 | var packedType = _buffer[_offset + _length * _byteWidth + index]; 469 | var elemOffset = _offset + (index * _byteWidth); 470 | return new FlxValue(_buffer, elemOffset, _byteWidth, packedType); 471 | } 472 | throw new Exception($"Bad index {index}, should be 0...{_length}"); 473 | } 474 | } 475 | 476 | public string ToJson 477 | { 478 | get 479 | { 480 | var builder = new StringBuilder(); 481 | builder.Append("["); 482 | for (var i = 0; i < _length; i++) 483 | { 484 | builder.Append(this[i].ToJson); 485 | if (i < _length - 1) 486 | { 487 | builder.Append(","); 488 | } 489 | } 490 | 491 | builder.Append("]"); 492 | 493 | return builder.ToString(); 494 | } 495 | } 496 | 497 | public string ToPrettyJson(string left = "", bool childrenOnly = false) 498 | { 499 | var builder = new StringBuilder(); 500 | if (childrenOnly == false) 501 | { 502 | builder.Append(left); 503 | } 504 | 505 | builder.Append("[\n"); 506 | for (var i = 0; i < _length; i++) 507 | { 508 | builder.Append(this[i].ToPrettyJson($"{left} ")); 509 | if (i < _length - 1) 510 | { 511 | builder.Append(","); 512 | } 513 | 514 | builder.Append("\n"); 515 | } 516 | builder.Append(left); 517 | builder.Append("]"); 518 | 519 | return builder.ToString(); 520 | } 521 | 522 | public IEnumerator GetEnumerator() 523 | { 524 | for (var i = 0; i < _length; i++) 525 | { 526 | yield return this[i]; 527 | } 528 | } 529 | 530 | IEnumerator IEnumerable.GetEnumerator() 531 | { 532 | return GetEnumerator(); 533 | } 534 | } 535 | 536 | public struct FlxMap: IEnumerable> 537 | { 538 | private readonly byte[] _buffer; 539 | private readonly int _offset; 540 | private readonly int _length; 541 | private readonly byte _byteWidth; 542 | 543 | internal FlxMap(byte[] buffer, int offset, byte byteWidth, int length) 544 | { 545 | _buffer = buffer; 546 | _offset = offset; 547 | _byteWidth = byteWidth; 548 | _length = length; 549 | } 550 | 551 | public int Length => _length; 552 | 553 | private FlxVector Keys 554 | { 555 | get 556 | { 557 | var keysOffset = _offset - _byteWidth * 3; 558 | var indirectOffset = FlxValue.ComputeIndirectOffset(_buffer, keysOffset, _byteWidth); 559 | var bWidth = FlxValue.ReadULong(_buffer, keysOffset + _byteWidth, _byteWidth); 560 | return new FlxVector(_buffer, indirectOffset, (byte) bWidth, Type.VectorKey, _length); 561 | } 562 | } 563 | 564 | private FlxVector Values => new FlxVector(_buffer, _offset, _byteWidth, Type.Vector, _length); 565 | 566 | public FlxValue this[string key] 567 | { 568 | get 569 | { 570 | var index = KeyIndex(key); 571 | if (index < 0) 572 | { 573 | throw new Exception($"No key '{key}' could be found"); 574 | } 575 | return Values[index]; 576 | } 577 | } 578 | 579 | public FlxValue ValueByIndex(int keyIndex) 580 | { 581 | if (keyIndex < 0 || keyIndex >= Length) 582 | { 583 | throw new Exception($"Bad Key index {keyIndex}"); 584 | } 585 | 586 | return Values[keyIndex]; 587 | } 588 | 589 | public string ToJson 590 | { 591 | get 592 | { 593 | var builder = new StringBuilder(); 594 | builder.Append("{"); 595 | var keys = Keys; 596 | var values = Values; 597 | for (var i = 0; i < _length; i++) 598 | { 599 | builder.Append($"{keys[i].ToJson}:{values[i].ToJson}"); 600 | if (i < _length - 1) 601 | { 602 | builder.Append(","); 603 | } 604 | } 605 | builder.Append("}"); 606 | return builder.ToString(); 607 | } 608 | } 609 | 610 | public string ToPrettyJson(string left = "", bool childrenOnly = false) 611 | { 612 | var builder = new StringBuilder(); 613 | if (childrenOnly == false) 614 | { 615 | builder.Append(left); 616 | } 617 | builder.Append("{\n"); 618 | var keys = Keys; 619 | var values = Values; 620 | for (var i = 0; i < _length; i++) 621 | { 622 | builder.Append($"{left} {keys[i].ToPrettyJson()} : {values[i].ToPrettyJson($"{left} ", true)}"); 623 | if (i < _length - 1) 624 | { 625 | builder.Append(","); 626 | } 627 | 628 | builder.Append("\n"); 629 | } 630 | builder.Append(left); 631 | builder.Append("}"); 632 | return builder.ToString(); 633 | } 634 | 635 | public int KeyIndex(string key) 636 | { 637 | var keyBytes = Encoding.UTF8.GetBytes(key); 638 | var low = 0; 639 | var high = _length - 1; 640 | while (low <= high) 641 | { 642 | var mid = (high + low) >> 1; 643 | var dif = Comp(mid, keyBytes); 644 | if (dif == 0) 645 | { 646 | return mid; 647 | } 648 | if (dif < 0) 649 | { 650 | high = mid - 1; 651 | } else 652 | { 653 | low = mid + 1; 654 | } 655 | } 656 | 657 | return -1; 658 | } 659 | 660 | private int Comp(int i, string key) 661 | { 662 | // TODO: keep it so we can profile it against byte comparison 663 | var key2 = Keys[i].AsString; 664 | return string.Compare(key, key2, StringComparison.Ordinal); 665 | } 666 | 667 | private int Comp(int i, byte[] key) 668 | { 669 | var key2 = Keys[i]; 670 | var indirectOffset = key2.IndirectOffset; 671 | for (int j = 0; j < key.Length; j++) 672 | { 673 | var dif = key[j] - key2.Buffer[indirectOffset + j]; 674 | if (dif != 0) 675 | { 676 | return dif; 677 | } 678 | } 679 | // keys are zero terminated 680 | return key2.Buffer[indirectOffset + key.Length] == 0 ? 0 : -1; 681 | } 682 | 683 | public IEnumerator> GetEnumerator() 684 | { 685 | var keys = Keys; 686 | var values = Values; 687 | for (var i = 0; i < _length; i++) 688 | { 689 | yield return new KeyValuePair(keys[i].AsString, values[i]); 690 | } 691 | } 692 | 693 | IEnumerator IEnumerable.GetEnumerator() 694 | { 695 | return GetEnumerator(); 696 | } 697 | } 698 | } -------------------------------------------------------------------------------- /FlexBuffers/JsonToFlexBufferConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using System.Globalization; 5 | 6 | // This code is based on LightJson project. 7 | // https://github.com/MarcosLopezC/LightJson 8 | // Big thanks goes to Marcos Vladimir López Castellanos 9 | // https://github.com/MarcosLopezC 10 | namespace FlexBuffers 11 | { 12 | 13 | 14 | public class JsonToFlexBufferConverter 15 | { 16 | private readonly TextScanner _scanner; 17 | 18 | private JsonToFlexBufferConverter(TextReader reader) 19 | { 20 | _scanner = new TextScanner(reader); 21 | } 22 | public static byte[] Convert(TextReader reader, FlexBuffer.Options options = FlexBuffer.Options.ShareKeys | FlexBuffer.Options.ShareStrings | FlexBuffer.Options.ShareKeyVectors) 23 | { 24 | if (reader == null) 25 | { 26 | throw new ArgumentNullException(nameof(reader)); 27 | } 28 | var flx = new FlexBuffer(options:options); 29 | new JsonToFlexBufferConverter(reader).ReadJsonValue(flx); 30 | return flx.Finish(); 31 | } 32 | 33 | public static byte[] ConvertFile(string path, FlexBuffer.Options options = FlexBuffer.Options.ShareKeys | FlexBuffer.Options.ShareStrings | FlexBuffer.Options.ShareKeyVectors) 34 | { 35 | if (path == null) 36 | { 37 | throw new ArgumentNullException(nameof(path)); 38 | } 39 | 40 | using (var reader = new StreamReader(path)) 41 | { 42 | return Convert(reader, options); 43 | } 44 | } 45 | 46 | public static byte[] Convert(string source, FlexBuffer.Options options = FlexBuffer.Options.ShareKeys | FlexBuffer.Options.ShareStrings | FlexBuffer.Options.ShareKeyVectors) 47 | { 48 | if (source == null) 49 | { 50 | throw new ArgumentNullException(nameof(source)); 51 | } 52 | 53 | using (var reader = new StringReader(source)) 54 | { 55 | return Convert(reader, options); 56 | } 57 | } 58 | 59 | private void ReadJsonValue(FlexBuffer flx) 60 | { 61 | _scanner.SkipWhitespace(); 62 | 63 | var next = _scanner.Peek(); 64 | 65 | if (char.IsNumber(next)) 66 | { 67 | ReadNumber(flx); 68 | return; 69 | } 70 | 71 | switch (next) 72 | { 73 | case '{': 74 | ReadObject(flx); 75 | return; 76 | 77 | case '[': 78 | ReadArray(flx); 79 | return; 80 | 81 | case '"': 82 | ReadString(flx); 83 | return; 84 | 85 | case '-': 86 | ReadNumber(flx); 87 | return; 88 | 89 | case 't': 90 | case 'f': 91 | ReadBoolean(flx); 92 | return; 93 | 94 | case 'n': 95 | ReadNull(flx); 96 | return; 97 | 98 | default: 99 | throw new Exception($"Unexpected character {_scanner.Position}"); 100 | } 101 | } 102 | 103 | private void ReadNull(FlexBuffer flx) 104 | { 105 | _scanner.Assert("null"); 106 | flx.AddNull(); 107 | } 108 | 109 | private void ReadBoolean(FlexBuffer flx) 110 | { 111 | switch (_scanner.Peek()) 112 | { 113 | case 't': 114 | _scanner.Assert("true"); 115 | flx.Add(true); 116 | return; 117 | 118 | case 'f': 119 | _scanner.Assert("false"); 120 | flx.Add(false); 121 | return; 122 | 123 | default: 124 | throw new Exception($"Unexpected character {_scanner.Position}"); 125 | } 126 | } 127 | 128 | private void ReadDigits(StringBuilder builder) 129 | { 130 | while (_scanner.CanRead && char.IsDigit(_scanner.Peek())) 131 | { 132 | builder.Append(_scanner.Read()); 133 | } 134 | } 135 | 136 | private void ReadNumber(FlexBuffer flx) 137 | { 138 | var builder = new StringBuilder(); 139 | 140 | var isFloat = false; 141 | 142 | if (_scanner.Peek() == '-') 143 | { 144 | builder.Append(_scanner.Read()); 145 | } 146 | 147 | if (_scanner.Peek() == '0') 148 | { 149 | builder.Append(_scanner.Read()); 150 | } 151 | else 152 | { 153 | ReadDigits(builder); 154 | } 155 | 156 | if (_scanner.CanRead && _scanner.Peek() == '.') 157 | { 158 | builder.Append(_scanner.Read()); 159 | ReadDigits(builder); 160 | isFloat = true; 161 | } 162 | 163 | if (_scanner.CanRead && char.ToLowerInvariant(_scanner.Peek()) == 'e') 164 | { 165 | builder.Append(_scanner.Read()); 166 | 167 | var next = _scanner.Peek(); 168 | 169 | switch (next) 170 | { 171 | case '+': 172 | case '-': 173 | builder.Append(_scanner.Read()); 174 | break; 175 | } 176 | 177 | ReadDigits(builder); 178 | } 179 | 180 | if (isFloat) 181 | { 182 | var value = double.Parse( 183 | builder.ToString(), 184 | CultureInfo.InvariantCulture 185 | ); 186 | flx.Add(value); 187 | } 188 | else 189 | { 190 | flx.Add(long.Parse(builder.ToString())); 191 | } 192 | } 193 | 194 | private void ReadString(FlexBuffer flx, bool asKey = false) 195 | { 196 | var builder = new StringBuilder(); 197 | 198 | _scanner.Assert('"'); 199 | 200 | while (true) 201 | { 202 | var c = _scanner.Read(); 203 | 204 | if (c == '\\') 205 | { 206 | c = _scanner.Read(); 207 | 208 | switch (char.ToLower(c)) 209 | { 210 | case '"': // " 211 | case '\\': // \ 212 | case '/': // / 213 | builder.Append(c); 214 | break; 215 | case 'b': 216 | builder.Append('\b'); 217 | break; 218 | case 'f': 219 | builder.Append('\f'); 220 | break; 221 | case 'n': 222 | builder.Append('\n'); 223 | break; 224 | case 'r': 225 | builder.Append('\r'); 226 | break; 227 | case 't': 228 | builder.Append('\t'); 229 | break; 230 | case 'u': 231 | builder.Append(ReadUnicodeLiteral()); 232 | break; 233 | default: 234 | throw new Exception($"Unexpected character {_scanner.Position}"); 235 | } 236 | } 237 | else if (c == '"') 238 | { 239 | break; 240 | } 241 | else 242 | { 243 | if (char.IsControl(c)) 244 | { 245 | throw new Exception($"Unexpected character {_scanner.Position}"); 246 | } 247 | else 248 | { 249 | builder.Append(c); 250 | } 251 | } 252 | } 253 | 254 | if (asKey) 255 | { 256 | flx.AddKey(builder.ToString()); 257 | } 258 | else 259 | { 260 | flx.Add(builder.ToString()); 261 | } 262 | } 263 | 264 | private int ReadHexDigit() 265 | { 266 | switch (char.ToUpper(_scanner.Read())) 267 | { 268 | case '0': 269 | return 0; 270 | 271 | case '1': 272 | return 1; 273 | 274 | case '2': 275 | return 2; 276 | 277 | case '3': 278 | return 3; 279 | 280 | case '4': 281 | return 4; 282 | 283 | case '5': 284 | return 5; 285 | 286 | case '6': 287 | return 6; 288 | 289 | case '7': 290 | return 7; 291 | 292 | case '8': 293 | return 8; 294 | 295 | case '9': 296 | return 9; 297 | 298 | case 'A': 299 | return 10; 300 | 301 | case 'B': 302 | return 11; 303 | 304 | case 'C': 305 | return 12; 306 | 307 | case 'D': 308 | return 13; 309 | 310 | case 'E': 311 | return 14; 312 | 313 | case 'F': 314 | return 15; 315 | 316 | default: 317 | throw new Exception($"Unexpected character {_scanner.Position}"); 318 | } 319 | } 320 | 321 | private char ReadUnicodeLiteral() 322 | { 323 | int value = 0; 324 | 325 | value += ReadHexDigit() * 4096; // 16^3 326 | value += ReadHexDigit() * 256; // 16^2 327 | value += ReadHexDigit() * 16; // 16^1 328 | value += ReadHexDigit(); // 16^0 329 | 330 | return (char)value; 331 | } 332 | 333 | private void ReadArray(FlexBuffer flx) 334 | { 335 | _scanner.Assert('['); 336 | 337 | var start = flx.StartVector(); 338 | 339 | _scanner.SkipWhitespace(); 340 | 341 | if (_scanner.Peek() == ']') 342 | { 343 | _scanner.Read(); 344 | } 345 | else 346 | { 347 | while (true) 348 | { 349 | ReadJsonValue(flx); 350 | 351 | _scanner.SkipWhitespace(); 352 | 353 | var next = _scanner.Read(); 354 | 355 | if (next == ']') 356 | { 357 | break; 358 | } 359 | 360 | if (next == ',') 361 | { 362 | continue; 363 | } 364 | 365 | throw new Exception($"Unexpected character {next} at position {_scanner.Position}"); 366 | } 367 | } 368 | 369 | flx.EndVector(start, false, false); 370 | } 371 | 372 | private void ReadObject(FlexBuffer flx) 373 | { 374 | _scanner.Assert('{'); 375 | 376 | _scanner.SkipWhitespace(); 377 | 378 | var start = flx.StartVector(); 379 | 380 | if (_scanner.Peek() == '}') 381 | { 382 | _scanner.Read(); 383 | } 384 | else 385 | { 386 | while (true) 387 | { 388 | _scanner.SkipWhitespace(); 389 | 390 | ReadString(flx, true); 391 | 392 | _scanner.SkipWhitespace(); 393 | 394 | _scanner.Assert(':'); 395 | 396 | _scanner.SkipWhitespace(); 397 | 398 | ReadJsonValue(flx); 399 | 400 | _scanner.SkipWhitespace(); 401 | 402 | var next = _scanner.Read(); 403 | 404 | if (next == '}') 405 | { 406 | break; 407 | } 408 | 409 | if (next == ',') 410 | { 411 | continue; 412 | } 413 | throw new Exception($"Unexpected character {next} at position {_scanner.Position}"); 414 | } 415 | } 416 | 417 | flx.SortAndEndMap(start); 418 | } 419 | } 420 | } -------------------------------------------------------------------------------- /FlexBuffers/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("FlexBuffers")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("FlexBuffers")] 12 | [assembly: AssemblyCopyright("Copyright © 2019")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("413DE2E2-0412-4EEE-AB96-BB4EF5EECAE1")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /FlexBuffers/StackValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace FlexBuffers 5 | { 6 | [StructLayout(LayoutKind.Explicit, Size=10)] 7 | public struct StackValue 8 | { 9 | [FieldOffset(0)] private ulong UValue; 10 | 11 | [FieldOffset(0)] private long LValue; 12 | 13 | [FieldOffset(0)] private double DValue; 14 | 15 | [FieldOffset(8)] private BitWidth Width; 16 | 17 | [FieldOffset(9)] private Type ValueType; 18 | 19 | public static StackValue Null() 20 | { 21 | return new StackValue 22 | { 23 | Width = BitWidth.Width8, 24 | LValue = 0, 25 | ValueType = Type.Null 26 | }; 27 | } 28 | 29 | public static StackValue Value(float value) 30 | { 31 | return new StackValue 32 | { 33 | Width = BitWidth.Width32, 34 | DValue = value, 35 | ValueType = Type.Float 36 | }; 37 | } 38 | 39 | public static StackValue Value(double value) 40 | { 41 | return new StackValue 42 | { 43 | Width = BitWidthUtil.Width(value), 44 | DValue = value, 45 | ValueType = Type.Float 46 | }; 47 | } 48 | 49 | public static StackValue Value(bool value) 50 | { 51 | return new StackValue 52 | { 53 | Width = BitWidth.Width8, 54 | LValue = value ? 1 : 0, 55 | ValueType = Type.Bool 56 | }; 57 | } 58 | 59 | public static StackValue Value(long value) 60 | { 61 | return new StackValue 62 | { 63 | Width = BitWidthUtil.Width(value), 64 | LValue = value, 65 | ValueType = Type.Int 66 | }; 67 | } 68 | 69 | public static StackValue Value(ulong value) 70 | { 71 | return new StackValue 72 | { 73 | Width = BitWidthUtil.Width(value), 74 | UValue = value, 75 | ValueType = Type.Uint 76 | }; 77 | } 78 | 79 | public static StackValue Value(ulong value, BitWidth width, Type type) 80 | { 81 | return new StackValue 82 | { 83 | Width = width, 84 | UValue = value, 85 | ValueType = type 86 | }; 87 | } 88 | 89 | public static StackValue Value(long value, BitWidth width, Type type) 90 | { 91 | return new StackValue 92 | { 93 | Width = width, 94 | LValue = value, 95 | ValueType = type 96 | }; 97 | } 98 | 99 | public BitWidth StoredWidth(BitWidth bitWidth = BitWidth.Width8) 100 | { 101 | if (TypesUtil.IsInline(ValueType)) 102 | { 103 | return (BitWidth) Math.Max((int) bitWidth, (int) Width); 104 | } 105 | 106 | return Width; 107 | } 108 | 109 | public byte StoredPackedType(BitWidth bitWidth = BitWidth.Width8) 110 | { 111 | return TypesUtil.PackedType(ValueType, StoredWidth(bitWidth)); 112 | } 113 | 114 | public BitWidth ElementWidth(ulong size, int index) 115 | { 116 | if (TypesUtil.IsInline(ValueType)) 117 | { 118 | return Width; 119 | } 120 | 121 | for (var i = 0; i < 4; i++) 122 | { 123 | var width = (ulong)1 << i; 124 | var offsetLoc = size + BitWidthUtil.PaddingSize(size, width) + (ulong)index * width; 125 | var offset = offsetLoc - UValue; 126 | var bitWidth = BitWidthUtil.Width(offset); 127 | if ((1UL << (byte) bitWidth) == width) 128 | { 129 | return bitWidth; 130 | } 131 | } 132 | throw new Exception($"Element with size: {size} and index: {index} is of unknown width"); 133 | } 134 | 135 | public long AsLong => LValue; 136 | public ulong AsULong => UValue; 137 | public double AsDouble => DValue; 138 | 139 | public bool IsFloat32 => ValueType == Type.Float && Width == BitWidth.Width32; 140 | public bool IsOffset => TypesUtil.IsInline(ValueType) == false; 141 | 142 | public Type TypeOfValue => ValueType; 143 | public BitWidth InternalWidth => Width; 144 | } 145 | } -------------------------------------------------------------------------------- /FlexBuffers/TextScanner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace FlexBuffers 5 | { 6 | internal struct TextPosition 7 | { 8 | public long column; 9 | public long line; 10 | public override string ToString() 11 | { 12 | return $"line: {line} column: {column}"; 13 | } 14 | } 15 | 16 | internal sealed class TextScanner 17 | { 18 | private readonly TextReader _reader; 19 | private TextPosition _position; 20 | 21 | public TextPosition Position => _position; 22 | 23 | public bool CanRead => (_reader.Peek() != -1); 24 | 25 | internal TextScanner(TextReader reader) 26 | { 27 | _reader = reader ?? throw new ArgumentNullException(nameof(reader)); 28 | } 29 | 30 | internal char Peek() 31 | { 32 | var next = _reader.Peek(); 33 | 34 | if (next == -1) 35 | { 36 | throw new Exception($"Incomplete message {_position}"); 37 | } 38 | 39 | return (char)next; 40 | } 41 | 42 | internal char Read() 43 | { 44 | var next = _reader.Read(); 45 | 46 | if (next == -1) 47 | { 48 | throw new Exception($"Incomplete message {_position}"); 49 | } 50 | 51 | switch (next) 52 | { 53 | case '\r': 54 | // Normalize '\r\n' line encoding to '\n'. 55 | if (_reader.Peek() == '\n') 56 | { 57 | _reader.Read(); 58 | } 59 | goto case '\n'; 60 | 61 | case '\n': 62 | _position.line += 1; 63 | _position.column = 0; 64 | return '\n'; 65 | 66 | default: 67 | _position.column += 1; 68 | return (char)next; 69 | } 70 | } 71 | 72 | internal void SkipWhitespace() 73 | { 74 | while (char.IsWhiteSpace(Peek())) 75 | { 76 | Read(); 77 | } 78 | } 79 | 80 | internal void Assert(char next) 81 | { 82 | if (Peek() == next) 83 | { 84 | Read(); 85 | } 86 | else 87 | { 88 | throw new Exception($"Parser expected {next} at position {_position}"); 89 | } 90 | } 91 | 92 | public void Assert(string next) 93 | { 94 | for (var i = 0; i < next.Length; i += 1) 95 | { 96 | Assert(next[i]); 97 | } 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /FlexBuffers/Types.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FlexBuffers 4 | { 5 | public enum Type: byte 6 | { 7 | Null, Int, Uint, Float, 8 | Key, String, IndirectInt, IndirectUInt, IndirectFloat, 9 | Map, Vector, VectorInt, VectorUInt, VectorFloat, VectorKey, VectorString, 10 | VectorInt2, VectorUInt2, VectorFloat2, 11 | VectorInt3, VectorUInt3, VectorFloat3, 12 | VectorInt4, VectorUInt4, VectorFloat4, 13 | Blob, Bool, VectorBool = 36 14 | } 15 | 16 | public static class TypesUtil 17 | { 18 | public static bool IsInline(Type type) 19 | { 20 | return type == Type.Bool || (byte) type <= (byte) Type.Float; 21 | } 22 | 23 | public static bool IsTypedVectorElement(Type type) 24 | { 25 | var typeValue = (byte) type; 26 | return type == Type.Bool || (typeValue >= (byte) Type.Int && typeValue <= (byte) Type.String); 27 | } 28 | 29 | public static bool IsTypedVector(Type type) 30 | { 31 | var typeValue = (byte) type; 32 | return type == Type.VectorBool || (typeValue >= (byte) Type.VectorInt && typeValue <= (byte) Type.VectorString); 33 | } 34 | 35 | public static bool IsFixedTypedVector(Type type) 36 | { 37 | var typeValue = (byte) type; 38 | return (typeValue >= (byte) Type.VectorInt2 && typeValue <= (byte) Type.VectorFloat4); 39 | } 40 | 41 | public static bool IsAVector(Type type) 42 | { 43 | return IsTypedVector(type) || IsFixedTypedVector(type) || type == Type.Vector; 44 | } 45 | 46 | public static Type ToTypedVector(Type type, byte length) 47 | { 48 | var typeValue = (byte) type; 49 | if (length == 0) 50 | { 51 | return (Type) (typeValue - (byte) Type.Int + (byte) Type.VectorInt); 52 | } 53 | if (length == 2) 54 | { 55 | return (Type) (typeValue - (byte) Type.Int + (byte) Type.VectorInt2); 56 | } 57 | if (length == 3) 58 | { 59 | return (Type) (typeValue - (byte) Type.Int + (byte) Type.VectorInt3); 60 | } 61 | if (length == 4) 62 | { 63 | return (Type) (typeValue - (byte) Type.Int + (byte) Type.VectorInt4); 64 | } 65 | throw new Exception($"Unexpected length: {length}"); 66 | } 67 | 68 | public static Type TypedVectorElementType(Type type) 69 | { 70 | var typeValue = (byte) type; 71 | return (Type) (typeValue - (byte) Type.VectorInt + (byte) Type.Int); 72 | } 73 | 74 | public static Type FixedTypedVectorElementType(Type type) 75 | { 76 | var fixedType = (byte) type - (byte) Type.VectorInt2; 77 | return (Type)(fixedType % 3 + (int) Type.Int); 78 | } 79 | 80 | public static int FixedTypedVectorElementSize(Type type) 81 | { 82 | var fixedType = (byte) type - (byte) Type.VectorInt2; 83 | return fixedType / 3 + 2; 84 | } 85 | 86 | public static byte PackedType(Type type, BitWidth bitWidth) 87 | { 88 | return (byte) ((byte) bitWidth | ((byte)type << 2)); 89 | } 90 | 91 | public static byte NullPackedType() 92 | { 93 | return PackedType(Type.Null, BitWidth.Width8); 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /FlexBuffers/XmlToFlexBufferConverter.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Xml; 3 | 4 | namespace FlexBuffers 5 | { 6 | public static class XmlToFlexBufferConverter 7 | { 8 | public static byte[] Convert(string xmlData) 9 | { 10 | XmlDocument doc = new XmlDocument(); 11 | 12 | doc.Load(new StringReader(xmlData)); 13 | 14 | var flx = new FlexBuffer(); 15 | 16 | Process(flx, doc.DocumentElement); 17 | 18 | return flx.Finish(); 19 | } 20 | 21 | private static void Process(FlexBuffer flx, XmlNode element) 22 | { 23 | var node = flx.StartVector(); 24 | flx.AddKey("tagName"); 25 | flx.Add(element.Name); 26 | var attributes = element.Attributes; 27 | if (attributes != null) 28 | { 29 | for (var i = 0; i < attributes.Count; i++) 30 | { 31 | var att = attributes.Item(i); 32 | flx.AddKey(att.Name); 33 | flx.Add(att.Value); 34 | } 35 | } 36 | 37 | var children = element.ChildNodes; 38 | if (children.Count > 0) 39 | { 40 | flx.AddKey("children"); 41 | var childVector = flx.StartVector(); 42 | for (var i = 0; i < children.Count; i++) 43 | { 44 | var child = children[i]; 45 | if (child.NodeType == XmlNodeType.Text || child.NodeType == XmlNodeType.CDATA) 46 | { 47 | flx.Add(child.Value); 48 | } else if (child.NodeType == XmlNodeType.Comment) 49 | { 50 | 51 | } else 52 | { 53 | Process(flx, child); 54 | } 55 | } 56 | 57 | flx.EndVector(childVector, false, false); 58 | } 59 | flx.SortAndEndMap(node); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /FlexBuffersCSharp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlexBuffers", "FlexBuffers\FlexBuffers.csproj", "{413DE2E2-0412-4EEE-AB96-BB4EF5EECAE1}" 4 | EndProject 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlexBuffersCSharpTests", "FlexBuffersCSharpTests\FlexBuffersCSharpTests.csproj", "{16A313A0-E208-4A82-8464-628C008E77CB}" 6 | EndProject 7 | Global 8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 9 | Debug|Any CPU = Debug|Any CPU 10 | Release|Any CPU = Release|Any CPU 11 | EndGlobalSection 12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 13 | {413DE2E2-0412-4EEE-AB96-BB4EF5EECAE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 14 | {413DE2E2-0412-4EEE-AB96-BB4EF5EECAE1}.Debug|Any CPU.Build.0 = Debug|Any CPU 15 | {413DE2E2-0412-4EEE-AB96-BB4EF5EECAE1}.Release|Any CPU.ActiveCfg = Release|Any CPU 16 | {413DE2E2-0412-4EEE-AB96-BB4EF5EECAE1}.Release|Any CPU.Build.0 = Release|Any CPU 17 | {16A313A0-E208-4A82-8464-628C008E77CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 18 | {16A313A0-E208-4A82-8464-628C008E77CB}.Debug|Any CPU.Build.0 = Debug|Any CPU 19 | {16A313A0-E208-4A82-8464-628C008E77CB}.Release|Any CPU.ActiveCfg = Release|Any CPU 20 | {16A313A0-E208-4A82-8464-628C008E77CB}.Release|Any CPU.Build.0 = Release|Any CPU 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /FlexBuffersCSharpTests/BitWidthTests.cs: -------------------------------------------------------------------------------- 1 | using FlexBuffers; 2 | using NUnit.Framework; 3 | 4 | namespace FlexBuffersCSharpTests 5 | { 6 | [TestFixture] 7 | public class BitWidthTests 8 | { 9 | [Test] 10 | public void Zero() 11 | { 12 | Assert.AreEqual(BitWidth.Width8, BitWidthUtil.Width(0)); 13 | } 14 | 15 | [Test] 16 | public void One() 17 | { 18 | Assert.AreEqual(BitWidth.Width8, BitWidthUtil.Width(1)); 19 | } 20 | 21 | [Test] 22 | public void MinusOne() 23 | { 24 | Assert.AreEqual(BitWidth.Width8, BitWidthUtil.Width(-1)); 25 | } 26 | 27 | [Test] 28 | public void ZeroPointFiveAsDouble() 29 | { 30 | Assert.AreEqual(BitWidth.Width32, BitWidthUtil.Width(0.5)); 31 | } 32 | 33 | [Test] 34 | public void ZeroPointOne() 35 | { 36 | Assert.AreEqual(BitWidth.Width64, BitWidthUtil.Width(0.1)); 37 | } 38 | 39 | [Test] 40 | public void ZeroPointOneAsFloat() 41 | { 42 | Assert.AreEqual(BitWidth.Width32, BitWidthUtil.Width(0.1f)); 43 | } 44 | 45 | [Test] 46 | public void ByteMaxAndMin() 47 | { 48 | Assert.AreEqual(BitWidth.Width8, BitWidthUtil.Width(byte.MaxValue)); 49 | Assert.AreEqual(BitWidth.Width8, BitWidthUtil.Width(byte.MinValue)); 50 | } 51 | 52 | [Test] 53 | public void ShortMaxAndMin() 54 | { 55 | Assert.AreEqual(BitWidth.Width8, BitWidthUtil.Width((short)sbyte.MaxValue)); 56 | Assert.AreEqual(BitWidth.Width8, BitWidthUtil.Width((short)sbyte.MinValue)); 57 | Assert.AreEqual(BitWidth.Width16, BitWidthUtil.Width((short)byte.MaxValue)); 58 | Assert.AreEqual(BitWidth.Width16, BitWidthUtil.Width((short)short.MaxValue)); 59 | Assert.AreEqual(BitWidth.Width16, BitWidthUtil.Width((short)short.MinValue)); 60 | Assert.AreEqual(BitWidth.Width8, BitWidthUtil.Width((ushort)byte.MaxValue)); 61 | Assert.AreEqual(BitWidth.Width16, BitWidthUtil.Width((ushort)ushort.MaxValue)); 62 | } 63 | 64 | [Test] 65 | public void IntMaxAndMin() 66 | { 67 | Assert.AreEqual(BitWidth.Width8, BitWidthUtil.Width((int)sbyte.MaxValue)); 68 | Assert.AreEqual(BitWidth.Width8, BitWidthUtil.Width((int)sbyte.MinValue)); 69 | Assert.AreEqual(BitWidth.Width16, BitWidthUtil.Width((int)byte.MaxValue)); 70 | Assert.AreEqual(BitWidth.Width16, BitWidthUtil.Width((int)short.MaxValue)); 71 | Assert.AreEqual(BitWidth.Width16, BitWidthUtil.Width((int)short.MinValue)); 72 | Assert.AreEqual(BitWidth.Width32, BitWidthUtil.Width((int)ushort.MaxValue)); 73 | Assert.AreEqual(BitWidth.Width32, BitWidthUtil.Width((int)int.MaxValue)); 74 | Assert.AreEqual(BitWidth.Width32, BitWidthUtil.Width((int)int.MinValue)); 75 | } 76 | 77 | [Test] 78 | public void UIntMaxAndMin() 79 | { 80 | Assert.AreEqual(BitWidth.Width8, BitWidthUtil.Width((uint)byte.MaxValue)); 81 | Assert.AreEqual(BitWidth.Width8, BitWidthUtil.Width((uint)byte.MinValue)); 82 | Assert.AreEqual(BitWidth.Width16, BitWidthUtil.Width((uint)ushort.MaxValue)); 83 | Assert.AreEqual(BitWidth.Width32, BitWidthUtil.Width((uint)uint.MaxValue)); 84 | Assert.AreEqual(BitWidth.Width32, BitWidthUtil.Width((uint)int.MaxValue)); 85 | } 86 | 87 | [Test] 88 | public void LongMaxAndMin() 89 | { 90 | Assert.AreEqual(BitWidth.Width8, BitWidthUtil.Width((long)sbyte.MaxValue)); 91 | Assert.AreEqual(BitWidth.Width8, BitWidthUtil.Width((long)sbyte.MinValue)); 92 | Assert.AreEqual(BitWidth.Width16, BitWidthUtil.Width((long)byte.MaxValue)); 93 | Assert.AreEqual(BitWidth.Width16, BitWidthUtil.Width((long)short.MaxValue)); 94 | Assert.AreEqual(BitWidth.Width16, BitWidthUtil.Width((long)short.MinValue)); 95 | Assert.AreEqual(BitWidth.Width32, BitWidthUtil.Width((long)ushort.MaxValue)); 96 | Assert.AreEqual(BitWidth.Width32, BitWidthUtil.Width((long)int.MaxValue)); 97 | Assert.AreEqual(BitWidth.Width64, BitWidthUtil.Width((long)uint.MaxValue)); 98 | Assert.AreEqual(BitWidth.Width32, BitWidthUtil.Width((long)int.MinValue)); 99 | Assert.AreEqual(BitWidth.Width64, BitWidthUtil.Width((long)long.MaxValue)); 100 | Assert.AreEqual(BitWidth.Width64, BitWidthUtil.Width((long)long.MinValue)); 101 | } 102 | 103 | [Test] 104 | public void ULongMaxAndMin() 105 | { 106 | Assert.AreEqual(BitWidth.Width8, BitWidthUtil.Width((ulong)byte.MaxValue)); 107 | Assert.AreEqual(BitWidth.Width8, BitWidthUtil.Width((ulong)byte.MinValue)); 108 | Assert.AreEqual(BitWidth.Width16, BitWidthUtil.Width((ulong)ushort.MaxValue)); 109 | Assert.AreEqual(BitWidth.Width32, BitWidthUtil.Width((ulong)uint.MaxValue)); 110 | Assert.AreEqual(BitWidth.Width32, BitWidthUtil.Width((ulong)int.MaxValue)); 111 | Assert.AreEqual(BitWidth.Width64, BitWidthUtil.Width((ulong)ulong.MaxValue)); 112 | } 113 | 114 | [Test] 115 | public void PaddingSize() 116 | { 117 | Assert.AreEqual(0, BitWidthUtil.PaddingSize(0, 1)); 118 | Assert.AreEqual(0, BitWidthUtil.PaddingSize(7, 1)); 119 | Assert.AreEqual(1, BitWidthUtil.PaddingSize(7, 2)); 120 | Assert.AreEqual(1, BitWidthUtil.PaddingSize(7, 4)); 121 | Assert.AreEqual(3, BitWidthUtil.PaddingSize(5, 4)); 122 | Assert.AreEqual(7, BitWidthUtil.PaddingSize(1, 8)); 123 | } 124 | } 125 | } -------------------------------------------------------------------------------- /FlexBuffersCSharpTests/CsvToFlexBuffersConverterTests.cs: -------------------------------------------------------------------------------- 1 | using FlexBuffers; 2 | using NUnit.Framework; 3 | 4 | namespace FlexBuffersCSharpTests 5 | { 6 | [TestFixture] 7 | public class CsvToFlexBuffersConverterTests 8 | { 9 | [Test] 10 | public void SimpleCSV() 11 | { 12 | var bytes = CsvToFlexBufferConverter.Convert("A,B,C"); 13 | var flx = FlxValue.FromBytes(bytes); 14 | Assert.AreEqual(flx.AsVector.Length, 1); 15 | Assert.AreEqual(flx[0].AsVector.Length, 3); 16 | Assert.AreEqual(flx[0][0].AsString, "A"); 17 | Assert.AreEqual(flx[0][1].AsString, "B"); 18 | Assert.AreEqual(flx[0][2].AsString, "C"); 19 | } 20 | 21 | [Test] 22 | public void SimpleCSVWithEmptyValue() 23 | { 24 | var bytes = CsvToFlexBufferConverter.Convert("A,,C"); 25 | var flx = FlxValue.FromBytes(bytes); 26 | Assert.AreEqual(flx.AsVector.Length, 1); 27 | Assert.AreEqual(flx[0].AsVector.Length, 3); 28 | Assert.AreEqual(flx[0][0].AsString, "A"); 29 | Assert.AreEqual(flx[0][1].AsString, ""); 30 | Assert.AreEqual(flx[0][2].AsString, "C"); 31 | } 32 | 33 | [Test] 34 | public void SimpleCSVWithRN() 35 | { 36 | var bytes = CsvToFlexBufferConverter.Convert("A,B,C\r\n1,2,3"); 37 | var flx = FlxValue.FromBytes(bytes); 38 | Assert.AreEqual(flx.AsVector.Length, 2); 39 | Assert.AreEqual(flx[0].AsVector.Length, 3); 40 | Assert.AreEqual(flx[0][0].AsString, "A"); 41 | Assert.AreEqual(flx[0][1].AsString, "B"); 42 | Assert.AreEqual(flx[0][2].AsString, "C"); 43 | 44 | Assert.AreEqual(flx[1].AsVector.Length, 3); 45 | Assert.AreEqual(flx[1][0].AsString, "1"); 46 | Assert.AreEqual(flx[1][1].AsString, "2"); 47 | Assert.AreEqual(flx[1][2].AsString, "3"); 48 | } 49 | 50 | [Test] 51 | public void MultipleLineCSV() 52 | { 53 | var bytes = CsvToFlexBufferConverter.Convert( 54 | @"A,B,C 55 | 1,2,3 56 | dskfsdh,sdfsdf,sdfsf"); 57 | var flx = FlxValue.FromBytes(bytes); 58 | var json = flx.ToJson; 59 | Assert.AreEqual(flx.AsVector.Length, 3); 60 | Assert.AreEqual(flx[0].AsVector.Length, 3); 61 | Assert.AreEqual(flx[0][0].AsString, "A"); 62 | Assert.AreEqual(flx[0][1].AsString, "B"); 63 | Assert.AreEqual(flx[0][2].AsString, "C"); 64 | 65 | Assert.AreEqual(flx[1][0].AsString, "1"); 66 | Assert.AreEqual(flx[1][1].AsString, "2"); 67 | Assert.AreEqual(flx[1][2].AsString, "3"); 68 | 69 | Assert.AreEqual(flx[2][0].AsString, "dskfsdh"); 70 | Assert.AreEqual(flx[2][1].AsString, "sdfsdf"); 71 | Assert.AreEqual(flx[2][2].AsString, "sdfsf"); 72 | } 73 | 74 | [Test] 75 | public void MultipleLineWithNewLineAtTheEndCSV() 76 | { 77 | var bytes = CsvToFlexBufferConverter.Convert( 78 | @"A,B,C 79 | 1,2,3 80 | dskfsdh,sdfsdf,sdfsf 81 | "); 82 | var flx = FlxValue.FromBytes(bytes); 83 | var json = flx.ToJson; 84 | Assert.AreEqual(flx.AsVector.Length, 3); 85 | Assert.AreEqual(flx[0].AsVector.Length, 3); 86 | Assert.AreEqual(flx[0][0].AsString, "A"); 87 | Assert.AreEqual(flx[0][1].AsString, "B"); 88 | Assert.AreEqual(flx[0][2].AsString, "C"); 89 | 90 | Assert.AreEqual(flx[1][0].AsString, "1"); 91 | Assert.AreEqual(flx[1][1].AsString, "2"); 92 | Assert.AreEqual(flx[1][2].AsString, "3"); 93 | 94 | Assert.AreEqual(flx[2][0].AsString, "dskfsdh"); 95 | Assert.AreEqual(flx[2][1].AsString, "sdfsdf"); 96 | Assert.AreEqual(flx[2][2].AsString, "sdfsf"); 97 | } 98 | 99 | [Test] 100 | public void MultipleLineWithDoubleQuotes() 101 | { 102 | var bytes = CsvToFlexBufferConverter.Convert( 103 | @"A,B,C 104 | 1,2,3 105 | ""dskfsdh"",sdfsdf,sdfsf 106 | "); 107 | var flx = FlxValue.FromBytes(bytes); 108 | var json = flx.ToJson; 109 | Assert.AreEqual(flx.AsVector.Length, 3); 110 | Assert.AreEqual(flx[0].AsVector.Length, 3); 111 | Assert.AreEqual(flx[0][0].AsString, "A"); 112 | Assert.AreEqual(flx[0][1].AsString, "B"); 113 | Assert.AreEqual(flx[0][2].AsString, "C"); 114 | 115 | Assert.AreEqual(flx[1][0].AsString, "1"); 116 | Assert.AreEqual(flx[1][1].AsString, "2"); 117 | Assert.AreEqual(flx[1][2].AsString, "3"); 118 | 119 | Assert.AreEqual(flx[2][0].AsString, "dskfsdh"); 120 | Assert.AreEqual(flx[2][1].AsString, "sdfsdf"); 121 | Assert.AreEqual(flx[2][2].AsString, "sdfsf"); 122 | } 123 | 124 | [Test] 125 | public void MultipleLineWithDoubleQuotesEscapingComma() 126 | { 127 | var bytes = CsvToFlexBufferConverter.Convert( 128 | @"A,B,C 129 | 1,2,3 130 | ""ds,kfs,dh"",sdfsdf,sdfsf 131 | "); 132 | var flx = FlxValue.FromBytes(bytes); 133 | var json = flx.ToJson; 134 | Assert.AreEqual(flx.AsVector.Length, 3); 135 | Assert.AreEqual(flx[0].AsVector.Length, 3); 136 | Assert.AreEqual(flx[0][0].AsString, "A"); 137 | Assert.AreEqual(flx[0][1].AsString, "B"); 138 | Assert.AreEqual(flx[0][2].AsString, "C"); 139 | 140 | Assert.AreEqual(flx[1][0].AsString, "1"); 141 | Assert.AreEqual(flx[1][1].AsString, "2"); 142 | Assert.AreEqual(flx[1][2].AsString, "3"); 143 | 144 | Assert.AreEqual(flx[2][0].AsString, "ds,kfs,dh"); 145 | Assert.AreEqual(flx[2][1].AsString, "sdfsdf"); 146 | Assert.AreEqual(flx[2][2].AsString, "sdfsf"); 147 | } 148 | 149 | [Test] 150 | public void MultipleLineWithDoubleQuotesEscapingDoubleQuotes() 151 | { 152 | var bytes = CsvToFlexBufferConverter.Convert( 153 | @"A,B,C 154 | 1,2,3 155 | ""ds""""kfsdh"",sdfsdf,sdfsf 156 | "); 157 | var flx = FlxValue.FromBytes(bytes); 158 | var json = flx.ToJson; 159 | Assert.AreEqual(flx.AsVector.Length, 3); 160 | Assert.AreEqual(flx[0].AsVector.Length, 3); 161 | Assert.AreEqual(flx[0][0].AsString, "A"); 162 | Assert.AreEqual(flx[0][1].AsString, "B"); 163 | Assert.AreEqual(flx[0][2].AsString, "C"); 164 | 165 | Assert.AreEqual(flx[1][0].AsString, "1"); 166 | Assert.AreEqual(flx[1][1].AsString, "2"); 167 | Assert.AreEqual(flx[1][2].AsString, "3"); 168 | 169 | Assert.AreEqual(flx[2][0].AsString, "ds\"kfsdh"); 170 | Assert.AreEqual(flx[2][1].AsString, "sdfsdf"); 171 | Assert.AreEqual(flx[2][2].AsString, "sdfsf"); 172 | } 173 | 174 | [Test] 175 | public void MultipleLineWithDoubleQuotesEscapingLineBreak() 176 | { 177 | var bytes = CsvToFlexBufferConverter.Convert( 178 | @"A,B,C 179 | 1,2,3 180 | ""ds 181 | kfsdh"",sdfsdf,sdfsf 182 | "); 183 | var flx = FlxValue.FromBytes(bytes); 184 | var json = flx.ToJson; 185 | Assert.AreEqual(flx.AsVector.Length, 3); 186 | Assert.AreEqual(flx[0].AsVector.Length, 3); 187 | Assert.AreEqual(flx[0][0].AsString, "A"); 188 | Assert.AreEqual(flx[0][1].AsString, "B"); 189 | Assert.AreEqual(flx[0][2].AsString, "C"); 190 | 191 | Assert.AreEqual(flx[1][0].AsString, "1"); 192 | Assert.AreEqual(flx[1][1].AsString, "2"); 193 | Assert.AreEqual(flx[1][2].AsString, "3"); 194 | 195 | Assert.AreEqual(flx[2][0].AsString, "ds\nkfsdh"); 196 | Assert.AreEqual(flx[2][1].AsString, "sdfsdf"); 197 | Assert.AreEqual(flx[2][2].AsString, "sdfsf"); 198 | } 199 | 200 | [Test] 201 | public void MultipleLineWithSpecialSeparatorAndDoubleQuotesEscapingLineBreak() 202 | { 203 | var bytes = CsvToFlexBufferConverter.Convert( 204 | @"A;B;C 205 | 1;2;3 206 | ""ds 207 | kfsdh"";sdfsdf;sdfsf 208 | ", ';'); 209 | var flx = FlxValue.FromBytes(bytes); 210 | var json = flx.ToJson; 211 | Assert.AreEqual(flx.AsVector.Length, 3); 212 | Assert.AreEqual(flx[0].AsVector.Length, 3); 213 | Assert.AreEqual(flx[0][0].AsString, "A"); 214 | Assert.AreEqual(flx[0][1].AsString, "B"); 215 | Assert.AreEqual(flx[0][2].AsString, "C"); 216 | 217 | Assert.AreEqual(flx[1][0].AsString, "1"); 218 | Assert.AreEqual(flx[1][1].AsString, "2"); 219 | Assert.AreEqual(flx[1][2].AsString, "3"); 220 | 221 | Assert.AreEqual(flx[2][0].AsString, "ds\nkfsdh"); 222 | Assert.AreEqual(flx[2][1].AsString, "sdfsdf"); 223 | Assert.AreEqual(flx[2][2].AsString, "sdfsf"); 224 | } 225 | } 226 | } -------------------------------------------------------------------------------- /FlexBuffersCSharpTests/FlexBufferBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using FlexBuffers; 2 | using NUnit.Framework; 3 | 4 | namespace FlexBuffersCSharpTests 5 | { 6 | [TestFixture] 7 | public class FlexBufferBuilderTests 8 | { 9 | [Test] 10 | public void SmallIntVector() 11 | { 12 | var bytes = FlexBufferBuilder.Vector(builder => 13 | { 14 | builder.Add(1); 15 | builder.Add(2); 16 | builder.Add(3); 17 | builder.Add(4); 18 | builder.Add(5); 19 | }); 20 | 21 | var flx = FlxValue.FromBytes(bytes); 22 | Assert.AreEqual(5, flx.AsVector.Length); 23 | Assert.AreEqual(1, flx[0].AsLong); 24 | Assert.AreEqual(2, flx[1].AsLong); 25 | Assert.AreEqual(3, flx[2].AsLong); 26 | Assert.AreEqual(4, flx[3].AsLong); 27 | Assert.AreEqual(5, flx[4].AsLong); 28 | } 29 | 30 | [Test] 31 | public void SmallIntVectorAndNestedVector() 32 | { 33 | var bytes = FlexBufferBuilder.Vector(v1 => 34 | { 35 | v1.Add(1); 36 | v1.Add(2); 37 | v1.Add(3); 38 | v1.Add(4); 39 | v1.Add(5); 40 | v1.Vector(v2 => { 41 | v2.Add("hello"); 42 | v2.Add("world"); 43 | }); 44 | }); 45 | 46 | var flx = FlxValue.FromBytes(bytes); 47 | Assert.AreEqual(6, flx.AsVector.Length); 48 | Assert.AreEqual(1, flx[0].AsLong); 49 | Assert.AreEqual(2, flx[1].AsLong); 50 | Assert.AreEqual(3, flx[2].AsLong); 51 | Assert.AreEqual(4, flx[3].AsLong); 52 | Assert.AreEqual(5, flx[4].AsLong); 53 | Assert.AreEqual(2, flx[5].AsVector.Length); 54 | Assert.AreEqual("hello", flx[5][0].AsString); 55 | Assert.AreEqual("world", flx[5][1].AsString); 56 | } 57 | 58 | [Test] 59 | public void SmallMap() 60 | { 61 | var bytes = FlexBufferBuilder.Map(builder => 62 | { 63 | builder.Add("name", "Maxim"); 64 | builder.Add("age", 38); 65 | builder.Add("weight", 72.5); 66 | }); 67 | 68 | var flx = FlxValue.FromBytes(bytes); 69 | Assert.AreEqual(3, flx.AsMap.Length); 70 | Assert.AreEqual(38, flx["age"].AsLong); 71 | Assert.AreEqual(72.5, flx["weight"].AsDouble); 72 | Assert.AreEqual("Maxim", flx["name"].AsString); 73 | } 74 | 75 | [Test] 76 | public void ComplexMap() 77 | { 78 | var bytes = FlexBufferBuilder.Map(root => 79 | { 80 | root.Add("name", "Maxim"); 81 | root.Add("age", 38); 82 | root.Add("weight", 72.5); 83 | root.Map("address", address => 84 | { 85 | address.Add("city", "Bla"); 86 | address.Add("zip", "12345"); 87 | address.Add("countryCode", "XX"); 88 | }); 89 | root.Vector("flags", tags => 90 | { 91 | tags.Add(true); 92 | tags.Add(false); 93 | tags.Add(true); 94 | tags.Add(true); 95 | }); 96 | }); 97 | 98 | var flx = FlxValue.FromBytes(bytes); 99 | Assert.AreEqual(5, flx.AsMap.Length); 100 | 101 | Assert.AreEqual(38, flx["age"].AsLong); 102 | Assert.AreEqual(72.5, flx["weight"].AsDouble); 103 | Assert.AreEqual("Maxim", flx["name"].AsString); 104 | 105 | Assert.AreEqual(4, flx["flags"].AsVector.Length); 106 | Assert.AreEqual(true, flx["flags"][0].AsBool); 107 | Assert.AreEqual(false, flx["flags"][1].AsBool); 108 | Assert.AreEqual(true, flx["flags"][2].AsBool); 109 | Assert.AreEqual(true, flx["flags"][3].AsBool); 110 | 111 | Assert.AreEqual(3, flx["address"].AsMap.Length); 112 | Assert.AreEqual("Bla", flx["address"]["city"].AsString); 113 | Assert.AreEqual("12345", flx["address"]["zip"].AsString); 114 | Assert.AreEqual("XX", flx["address"]["countryCode"].AsString); 115 | } 116 | 117 | [Test] 118 | public void FixVectors() 119 | { 120 | var bytes = FlexBufferBuilder.Map(root => 121 | { 122 | root.Add("a", 1, 2); 123 | root.Add("b", 1, 2, 3); 124 | root.Add("c", 1, 2, 3, 4); 125 | root.Add("d", 1UL, 2); 126 | root.Add("e", 1UL, 2, 3); 127 | root.Add("f", 1UL, 2, 3, 4); 128 | root.Add("g", 1.1, 2); 129 | root.Add("h", 1.1, 2, 3); 130 | root.Add("i", 1.1, 2, 3, 4); 131 | root.Vector("j", vec => 132 | { 133 | vec.Add(1, 2); 134 | vec.Add(1, 2, 3); 135 | vec.Add(1, 2, 3, 4); 136 | vec.Add(1UL, 2); 137 | vec.Add(1UL, 2, 3); 138 | vec.Add(1UL, 2, 3, 4); 139 | vec.Add(1.1, 2); 140 | vec.Add(1.1, 2, 3); 141 | vec.Add(1.1, 2, 3, 4); 142 | }); 143 | }); 144 | 145 | var flx = FlxValue.FromBytes(bytes); 146 | Assert.AreEqual(10, flx.AsMap.Length); 147 | Assert.AreEqual(1, flx["a"][0].AsLong); 148 | Assert.AreEqual(2, flx["a"][1].AsLong); 149 | Assert.AreEqual(1, flx["b"][0].AsLong); 150 | Assert.AreEqual(2, flx["b"][1].AsLong); 151 | Assert.AreEqual(3, flx["b"][2].AsLong); 152 | Assert.AreEqual(1, flx["c"][0].AsLong); 153 | Assert.AreEqual(2, flx["c"][1].AsLong); 154 | Assert.AreEqual(3, flx["c"][2].AsLong); 155 | Assert.AreEqual(4, flx["c"][3].AsLong); 156 | Assert.AreEqual(1, flx["d"][0].AsULong); 157 | Assert.AreEqual(2, flx["d"][1].AsULong); 158 | Assert.AreEqual(1, flx["e"][0].AsULong); 159 | Assert.AreEqual(2, flx["e"][1].AsULong); 160 | Assert.AreEqual(3, flx["e"][2].AsULong); 161 | Assert.AreEqual(1, flx["f"][0].AsULong); 162 | Assert.AreEqual(2, flx["f"][1].AsULong); 163 | Assert.AreEqual(3, flx["f"][2].AsULong); 164 | Assert.AreEqual(4, flx["f"][3].AsULong); 165 | Assert.AreEqual(1.1, flx["g"][0].AsDouble); 166 | Assert.AreEqual(2, flx["g"][1].AsDouble); 167 | Assert.AreEqual(1.1, flx["h"][0].AsDouble); 168 | Assert.AreEqual(2, flx["h"][1].AsDouble); 169 | Assert.AreEqual(3, flx["h"][2].AsDouble); 170 | Assert.AreEqual(1.1, flx["i"][0].AsDouble); 171 | Assert.AreEqual(2, flx["i"][1].AsDouble); 172 | Assert.AreEqual(3, flx["i"][2].AsDouble); 173 | Assert.AreEqual(4, flx["i"][3].AsDouble); 174 | 175 | var v = flx["j"].AsVector; 176 | Assert.AreEqual(9, v.Length); 177 | Assert.AreEqual(1, v[0][0].AsLong); 178 | Assert.AreEqual(2, v[0][1].AsLong); 179 | Assert.AreEqual(1, v[1][0].AsLong); 180 | Assert.AreEqual(2, v[1][1].AsLong); 181 | Assert.AreEqual(3, v[1][2].AsLong); 182 | Assert.AreEqual(1, v[2][0].AsLong); 183 | Assert.AreEqual(2, v[2][1].AsLong); 184 | Assert.AreEqual(3, v[2][2].AsLong); 185 | Assert.AreEqual(4, v[2][3].AsLong); 186 | Assert.AreEqual(1, v[3][0].AsULong); 187 | Assert.AreEqual(2, v[3][1].AsULong); 188 | Assert.AreEqual(1, v[4][0].AsULong); 189 | Assert.AreEqual(2, v[4][1].AsULong); 190 | Assert.AreEqual(3, v[4][2].AsULong); 191 | Assert.AreEqual(1, v[5][0].AsULong); 192 | Assert.AreEqual(2, v[5][1].AsULong); 193 | Assert.AreEqual(3, v[5][2].AsULong); 194 | Assert.AreEqual(4, v[5][3].AsULong); 195 | Assert.AreEqual(1.1, v[6][0].AsDouble); 196 | Assert.AreEqual(2, v[6][1].AsDouble); 197 | Assert.AreEqual(1.1, v[7][0].AsDouble); 198 | Assert.AreEqual(2, v[7][1].AsDouble); 199 | Assert.AreEqual(3, v[7][2].AsDouble); 200 | Assert.AreEqual(1.1, v[8][0].AsDouble); 201 | Assert.AreEqual(2, v[8][1].AsDouble); 202 | Assert.AreEqual(3, v[8][2].AsDouble); 203 | Assert.AreEqual(4, v[8][3].AsDouble); 204 | } 205 | 206 | [Test] 207 | public void IndirectValuesInMap() 208 | { 209 | var bytes = FlexBufferBuilder.Map(root => 210 | { 211 | root.Add("a", -123, true); 212 | root.Add("b", 123UL, true); 213 | root.Add("c", 123.3, true); 214 | }); 215 | 216 | var flx = FlxValue.FromBytes(bytes); 217 | 218 | Assert.AreEqual(3, flx.AsMap.Length); 219 | Assert.AreEqual(-123, flx["a"].AsLong); 220 | Assert.AreEqual(123, flx["b"].AsULong); 221 | Assert.AreEqual(123.3, flx["c"].AsDouble); 222 | } 223 | 224 | [Test] 225 | public void IndirectValuesInMapToJson() 226 | { 227 | var bytes = FlexBufferBuilder.Map(root => 228 | { 229 | root.Add("a", -123, true); 230 | root.Add("b", 123UL, true); 231 | root.Add("c", 123.3, true); 232 | }); 233 | 234 | var flx = FlxValue.FromBytes(bytes); 235 | 236 | Assert.AreEqual( 237 | "{\"a\":-123,\"b\":123,\"c\":123.3}", 238 | flx.ToJson); 239 | } 240 | 241 | [Test] 242 | public void IndirectValuesInVector() 243 | { 244 | var bytes = FlexBufferBuilder.Vector(root => 245 | { 246 | root.Add(-123, true); 247 | root.Add(123UL, true); 248 | root.Add(123.3, true); 249 | }); 250 | 251 | var flx = FlxValue.FromBytes(bytes); 252 | 253 | Assert.AreEqual(3, flx.AsVector.Length); 254 | Assert.AreEqual(-123, flx[0].AsLong); 255 | Assert.AreEqual(123, flx[1].AsULong); 256 | Assert.AreEqual(123.3, flx[2].AsDouble); 257 | } 258 | 259 | [Test] 260 | public void IndirectValuesInVectorToJson() 261 | { 262 | var bytes = FlexBufferBuilder.Vector(root => 263 | { 264 | root.Add(-123, true); 265 | root.Add(123UL, true); 266 | root.Add(123.3, true); 267 | }); 268 | 269 | var flx = FlxValue.FromBytes(bytes); 270 | 271 | Assert.AreEqual( 272 | "[-123,123,123.3]", 273 | flx.ToJson); 274 | } 275 | 276 | [Test] 277 | public void AddNullAndBlobToNestedToJson() 278 | { 279 | var bytes = FlexBufferBuilder.Vector(root => 280 | { 281 | root.AddNull(); 282 | root.Add(new byte[]{1,2,3}); 283 | root.Map(map => 284 | { 285 | map.AddNull("a"); 286 | map.Add("b", new byte[]{3,4,5}); 287 | }); 288 | }); 289 | 290 | var flx = FlxValue.FromBytes(bytes); 291 | 292 | Assert.AreEqual( 293 | "[null,\"AQID\",{\"a\":null,\"b\":\"AwQF\"}]", 294 | flx.ToJson); 295 | } 296 | } 297 | } -------------------------------------------------------------------------------- /FlexBuffersCSharpTests/FlexBufferTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using FlexBuffers; 6 | using NUnit.Framework; 7 | 8 | namespace FlexBuffersCSharpTests 9 | { 10 | [TestFixture] 11 | public class FlexBufferTests 12 | { 13 | [Test] 14 | public void Null() 15 | { 16 | Check(new byte[]{0, 0, 1}, FlexBuffer.Null()); 17 | } 18 | 19 | [Test] 20 | public void Bool() 21 | { 22 | Check(new byte[]{1, 104, 1}, FlexBuffer.SingleValue(true)); 23 | Check(new byte[]{0, 104, 1}, FlexBuffer.SingleValue(false)); 24 | } 25 | 26 | [Test] 27 | public void OneByte() 28 | { 29 | Check(new byte[]{25, 4, 1}, FlexBuffer.SingleValue(25)); 30 | Check(new byte[]{231, 4, 1}, FlexBuffer.SingleValue(-25)); 31 | Check(new byte[]{230, 8, 1}, FlexBuffer.SingleValue(230UL)); 32 | } 33 | 34 | [Test] 35 | public void TwoBytes() 36 | { 37 | Check(new byte[]{230, 0, 5, 2}, FlexBuffer.SingleValue(230)); 38 | Check(new byte[]{1, 4, 5, 2}, FlexBuffer.SingleValue(1025)); 39 | Check(new byte[]{255, 251, 5, 2}, FlexBuffer.SingleValue(-1025)); 40 | Check(new byte[]{1, 4, 9, 2}, FlexBuffer.SingleValue(1025UL)); 41 | } 42 | 43 | [Test] 44 | public void FourBytes() 45 | { 46 | Check(new byte[]{255, 255, 255, 127, 6, 4}, FlexBuffer.SingleValue(int.MaxValue)); 47 | Check(new byte[]{0, 0, 0, 128, 6, 4}, FlexBuffer.SingleValue(int.MinValue)); 48 | Check(new byte[]{0, 0, 144, 64, 14, 4}, FlexBuffer.SingleValue(4.5)); 49 | Check(new byte[]{205, 204, 204, 61, 14, 4}, FlexBuffer.SingleValue(0.1f)); 50 | } 51 | 52 | [Test] 53 | public void EightBytes() 54 | { 55 | Check(new byte[]{255, 255, 255, 255, 0, 0, 0, 0, 7, 8}, FlexBuffer.SingleValue(uint.MaxValue)); 56 | Check(new byte[]{255, 255, 255, 255, 255, 255, 255, 127, 7, 8}, FlexBuffer.SingleValue(long.MaxValue)); 57 | Check(new byte[]{0, 0, 0, 0, 0, 0, 0, 128, 7, 8}, FlexBuffer.SingleValue(long.MinValue)); 58 | Check(new byte[]{255, 255, 255, 255, 255, 255, 255, 255, 11, 8}, FlexBuffer.SingleValue(ulong.MaxValue)); 59 | Check(new byte[]{154, 153, 153, 153, 153, 153, 185, 63, 15, 8}, FlexBuffer.SingleValue(0.1)); 60 | } 61 | 62 | [Test] 63 | public void String() 64 | { 65 | Check(new byte[]{5, 77, 97, 120, 105, 109, 0, 6, 20, 1}, FlexBuffer.SingleValue("Maxim")); 66 | Check(new byte[]{10, 104, 101, 108, 108, 111, 32, 240, 159, 152, 177, 0, 11, 20, 1}, FlexBuffer.SingleValue("hello 😱")); 67 | } 68 | 69 | [Test] 70 | public void IntVector() 71 | { 72 | Check(new byte[]{3, 1, 2, 3, 3, 44, 1}, FlexBuffer.From(new []{1, 2, 3})); 73 | Check(new byte[]{3, 255, 2, 3, 3, 44, 1}, FlexBuffer.From(new []{-1, 2, 3})); 74 | Check(new byte[]{3, 0, 1, 0, 43, 2, 3, 0, 6, 45, 1}, FlexBuffer.From(new []{1,555,3})); 75 | Check(new byte[]{3, 0, 0, 0, 1, 0, 0, 0, 204, 216, 0, 0, 3, 0, 0, 0, 12, 46, 1}, FlexBuffer.From(new []{1,55500,3})); 76 | Check(new byte[]{3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 172, 128, 94, 239, 12, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 24, 47, 1}, FlexBuffer.From(new []{1, 55555555500, 3})); 77 | } 78 | 79 | [Test] 80 | public void DoubleVector() 81 | { 82 | Check(new byte[]{3, 0, 0, 0, 0, 0, 192, 63, 0, 0, 32, 64, 0, 0, 96, 64, 12, 54, 1}, FlexBuffer.From(new []{1.5, 2.5, 3.5})); 83 | Check(new byte[]{3, 0, 0, 0, 0, 0, 0, 0, 154, 153, 153, 153, 153, 153, 241, 63, 154, 153, 153, 153, 153, 153, 1, 64, 102, 102, 102, 102, 102, 102, 10, 64, 24, 55, 1}, FlexBuffer.From(new []{1.1, 2.2, 3.3})); 84 | } 85 | 86 | [Test] 87 | public void BoolVector() 88 | { 89 | Check(new byte[]{3, 1, 0, 1, 3, 144, 1}, FlexBuffer.From(new []{true, false, true})); 90 | } 91 | 92 | [Test] 93 | public void StringVector() 94 | { 95 | Check(new byte[]{3, 102, 111, 111, 0, 3, 98, 97, 114, 0, 3, 98, 97, 122, 0, 3, 15, 11, 7, 3, 60, 1}, FlexBuffer.From(new []{"foo", "bar", "baz"})); 96 | } 97 | 98 | [Test] 99 | public void StringVectorWithRepeatingStrings() 100 | { 101 | Check(new byte[]{3, 102, 111, 111, 0, 3, 98, 97, 114, 0, 3, 98, 97, 122, 0, 6, 15, 11, 7, 18, 14, 10, 6, 60, 1}, FlexBuffer.From(new []{"foo", "bar", "baz", "foo", "bar", "baz"})); 102 | } 103 | 104 | [Test] 105 | public void MixedVector() 106 | { 107 | Check(new byte[] 108 | { 109 | 3, 102, 111, 111, 0, 0, 0, 0, 110 | 5, 0, 0, 0, 0, 0, 0, 0, 111 | 15, 0, 0, 0, 0, 0, 0, 0, 112 | 1, 0, 0, 0, 0, 0, 0, 0, 113 | 251, 255, 255, 255, 255, 255, 255, 255, 114 | 205, 204, 204, 204, 204, 204, 244, 63, 115 | 1, 0, 0, 0, 0, 0, 0, 0, 116 | 20, 4, 4, 15, 104, 45, 43, 1 117 | }, FlexBuffer.From(new List{"foo", 1, -5, 1.3, true})); 118 | } 119 | 120 | [Test] 121 | public void StringIntDictSingleValue() 122 | { 123 | var dict = new Dictionary() 124 | { 125 | {"a", 12} 126 | }; 127 | Check( 128 | new byte[]{97, 0, 1, 3, 1, 1, 1, 12, 4, 2, 36, 1}, 129 | FlexBuffer.From(dict)); 130 | } 131 | 132 | [Test] 133 | public void StringIntDict() 134 | { 135 | var dict = new Dictionary() 136 | { 137 | {"", 45}, 138 | {"a", 12} 139 | }; 140 | Check( 141 | new byte[]{0, 97, 0, 2, 4, 4, 2, 1, 2, 45, 12, 4, 4, 4, 36, 1}, 142 | FlexBuffer.From(dict)); 143 | } 144 | 145 | [Test] 146 | public void VectorOfSameKeyDicts() 147 | { 148 | var dict = new List>() 149 | { 150 | new Dictionary() 151 | { 152 | {"something", 12} 153 | }, 154 | new Dictionary() 155 | { 156 | {"something", 45} 157 | } 158 | }; 159 | Check( 160 | new byte[] 161 | { 162 | 115, 111, 109, 101, 116, 104, 105, 110, 103, 0, 163 | 1, 11, 1, 1, 1, 12, 4, 6, 1, 1, 45, 4, 2, 8, 4, 36, 36, 4, 40, 1 164 | }, 165 | FlexBuffer.From(dict)); 166 | } 167 | 168 | [Test] 169 | public void MixedVectorWithVectorAndInt() 170 | { 171 | var value = new List() 172 | { 173 | new []{61}, 174 | 64 175 | }; 176 | // Swift has {1, 61, 4, 2, 3, 64, 40, 4, 4, 40, 1} but it is also untyped 177 | Check( 178 | new byte[]{1, 61, 2, 2, 64, 44, 4, 4, 40, 1}, 179 | FlexBuffer.From(value)); 180 | } 181 | 182 | [Test] 183 | public void ComplexMap() 184 | { 185 | var value = new Dictionary() 186 | { 187 | {"age", 35}, 188 | {"flags", new bool[]{true, false, true, true}}, 189 | {"weight", 72.5}, 190 | {"name", "Maxim"}, 191 | {"address", new Dictionary() 192 | { 193 | {"city", "Bla"}, 194 | {"zip", "12345"}, 195 | {"countryCode", "XX"}, 196 | }}, 197 | }; 198 | // Different in Swift 199 | Check( 200 | new byte[] 201 | { 202 | 97, 103, 101, 0, 203 | 102, 108, 97, 103, 115, 0, 204 | 4, 1, 0, 1, 1, 205 | 119, 101, 105, 103, 104, 116, 0, 206 | 110, 97, 109, 101, 0, 207 | 5, 77, 97, 120, 105, 109, 0, 208 | 97, 100, 100, 114, 101, 115, 115, 0, 209 | 99, 105, 116, 121, 0, 210 | 3, 66, 108, 97, 0, 211 | 122, 105, 112, 0, 212 | 5, 49, 50, 51, 52, 53, 0, 213 | 99, 111, 117, 110, 116, 114, 121, 67, 111, 100, 101, 0, 214 | 2, 88, 88, 0, 215 | 3, 38, 18, 30, 3, 1, 3, 38, 11, 31, 20, 20, 20, 5, 59, 94, 91, 74, 82, 0, 0, 216 | 7, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 26, 0, 0, 0, 35, 0, 0, 0, 109, 0, 0, 0, 96, 0, 0, 0, 0, 0, 145, 66, 36, 4, 144, 20, 14, 25, 38, 1 217 | }, 218 | FlexBuffer.From(value)); 219 | } 220 | 221 | [Test] 222 | public void Long2() 223 | { 224 | Check( 225 | new byte[]{1, 2, 2, 64, 1}, 226 | FlexBuffer.SingleValue(1,2)); 227 | Check( 228 | new byte[]{255, 255, 0, 1, 4, 65, 1}, 229 | FlexBuffer.SingleValue(-1,256)); 230 | Check( 231 | new byte[]{211, 255, 255, 255, 0, 232, 3, 0, 8, 66, 1}, 232 | FlexBuffer.SingleValue(-45,256000)); 233 | Check( 234 | new byte[]{211, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, 16, 67, 1}, 235 | FlexBuffer.SingleValue(-45,long.MaxValue)); 236 | } 237 | 238 | [Test] 239 | public void ULong2() 240 | { 241 | Check( 242 | new byte[]{1, 2, 2, 68, 1}, 243 | FlexBuffer.SingleValue(1UL,2)); 244 | Check( 245 | new byte[]{1, 0, 0, 1, 4, 69, 1}, 246 | FlexBuffer.SingleValue(1UL,256)); 247 | Check( 248 | new byte[]{45, 0, 0, 0, 0, 232, 3, 0, 8, 70, 1}, 249 | FlexBuffer.SingleValue(45UL,256000)); 250 | Check( 251 | new byte[]{45, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 16, 71, 1}, 252 | FlexBuffer.SingleValue(45,ulong.MaxValue)); 253 | } 254 | 255 | [Test] 256 | public void Double2() 257 | { 258 | Check( 259 | new byte[]{205, 204, 140, 63, 0, 0, 0, 192, 8, 74, 1}, 260 | FlexBuffer.SingleValue(1.1f, -2)); 261 | Check( 262 | new byte[]{154, 153, 153, 153, 153, 153, 241, 63, 0, 0, 0, 0, 0, 0, 112, 192, 16, 75, 1}, 263 | FlexBuffer.SingleValue(1.1,-256)); 264 | } 265 | 266 | [Test] 267 | public void Long3() 268 | { 269 | Check( 270 | new byte[]{1, 2, 4, 3, 76, 1}, 271 | FlexBuffer.SingleValue(1, 2, 4)); 272 | Check( 273 | new byte[]{255, 255, 0, 1, 4, 0, 6, 77, 1}, 274 | FlexBuffer.SingleValue(-1,256, 4)); 275 | Check( 276 | new byte[] 277 | { 278 | 211, 255, 255, 255, 279 | 0, 232, 3, 0, 280 | 4, 0, 0, 0, 281 | 12, 78, 1 282 | }, 283 | FlexBuffer.SingleValue(-45,256000, 4)); 284 | Check( 285 | new byte[] 286 | { 287 | 211, 255, 255, 255, 255, 255, 255, 255, 288 | 255, 255, 255, 255, 255, 255, 255, 127, 289 | 4, 0, 0, 0, 0, 0, 0, 0, 290 | 24, 79, 1 291 | }, 292 | FlexBuffer.SingleValue(-45,long.MaxValue, 4)); 293 | } 294 | 295 | [Test] 296 | public void Long4() 297 | { 298 | Check( 299 | new byte[]{1, 2, 4, 9, 4, 88, 1}, 300 | FlexBuffer.SingleValue(1, 2, 4, 9)); 301 | Check( 302 | new byte[]{255, 255, 0, 1, 4, 0, 9, 0, 8, 89, 1}, 303 | FlexBuffer.SingleValue(-1,256, 4, 9)); 304 | Check( 305 | new byte[] 306 | { 307 | 211, 255, 255, 255, 308 | 0, 232, 3, 0, 309 | 4, 0, 0, 0, 310 | 9, 0, 0, 0, 311 | 16, 90, 1 312 | }, 313 | FlexBuffer.SingleValue(-45,256000, 4, 9)); 314 | Check( 315 | new byte[] 316 | { 317 | 211, 255, 255, 255, 255, 255, 255, 255, 318 | 255, 255, 255, 255, 255, 255, 255, 127, 319 | 4, 0, 0, 0, 0, 0, 0, 0, 320 | 9, 0, 0, 0, 0, 0, 0, 0, 321 | 32, 91, 1 322 | }, 323 | FlexBuffer.SingleValue(-45,long.MaxValue, 4, 9)); 324 | } 325 | 326 | [Test] 327 | public void ULong3() 328 | { 329 | Check( 330 | new byte[]{1, 2, 4, 3, 80, 1}, 331 | FlexBuffer.SingleValue(1UL, 2, 4)); 332 | Check( 333 | new byte[]{1, 0, 0, 1, 4, 0, 6, 81, 1}, 334 | FlexBuffer.SingleValue(1UL,256, 4)); 335 | Check( 336 | new byte[] 337 | { 338 | 45, 0, 0, 0, 339 | 0, 232, 3, 0, 340 | 4, 0, 0, 0, 341 | 12, 82, 1 342 | }, 343 | FlexBuffer.SingleValue(45UL,256000, 4)); 344 | Check( 345 | new byte[] 346 | { 347 | 45, 0, 0, 0, 0, 0, 0, 0, 348 | 255, 255, 255, 255, 255, 255, 255, 127, 349 | 4, 0, 0, 0, 0, 0, 0, 0, 350 | 24, 83, 1 351 | }, 352 | FlexBuffer.SingleValue(45UL,long.MaxValue, 4)); 353 | } 354 | 355 | [Test] 356 | public void ULong4() 357 | { 358 | Check( 359 | new byte[]{1, 2, 4, 9, 4, 92, 1}, 360 | FlexBuffer.SingleValue(1UL, 2, 4, 9)); 361 | Check( 362 | new byte[]{1, 0, 0, 1, 4, 0, 9, 0, 8, 93, 1}, 363 | FlexBuffer.SingleValue(1UL,256, 4, 9)); 364 | Check( 365 | new byte[] 366 | { 367 | 45, 0, 0, 0, 368 | 0, 232, 3, 0, 369 | 4, 0, 0, 0, 370 | 9, 0, 0, 0, 371 | 16, 94, 1 372 | }, 373 | FlexBuffer.SingleValue(45UL,256000, 4, 9)); 374 | Check( 375 | new byte[] 376 | { 377 | 45, 0, 0, 0, 0, 0, 0, 0, 378 | 255, 255, 255, 255, 255, 255, 255, 127, 379 | 4, 0, 0, 0, 0, 0, 0, 0, 380 | 9, 0, 0, 0, 0, 0, 0, 0, 381 | 32, 95, 1 382 | }, 383 | FlexBuffer.SingleValue(45UL,long.MaxValue, 4, 9)); 384 | } 385 | 386 | [Test] 387 | public void Double3() 388 | { 389 | Check( 390 | new byte[] 391 | { 392 | 205, 204, 140, 63, 393 | 0, 0, 0, 64, 394 | 0, 0, 128, 64, 395 | 12, 86, 1 396 | }, 397 | FlexBuffer.SingleValue(1.1f, 2, 4)); 398 | Check( 399 | new byte[] 400 | { 401 | 154, 153, 153, 153, 153, 153, 241, 63, 402 | 0, 0, 0, 0, 0, 0, 112, 64, 403 | 0, 0, 0, 0, 0, 0, 16, 64, 404 | 24, 87, 1 405 | }, 406 | FlexBuffer.SingleValue(1.1,256, 4)); 407 | } 408 | 409 | [Test] 410 | public void Double4() 411 | { 412 | Check( 413 | new byte[] 414 | { 415 | 205, 204, 140, 63, 416 | 0, 0, 0, 64, 417 | 0, 0, 128, 64, 418 | 0, 0, 16, 65, 419 | 16, 98, 1 420 | }, 421 | FlexBuffer.SingleValue(1.1f, 2, 4, 9)); 422 | Check( 423 | new byte[] 424 | { 425 | 154, 153, 153, 153, 153, 153, 241, 63, 426 | 0, 0, 0, 0, 0, 0, 112, 64, 427 | 0, 0, 0, 0, 0, 0, 16, 64, 428 | 0, 0, 0, 0, 0, 0, 34, 64, 429 | 32, 99, 1 430 | }, 431 | FlexBuffer.SingleValue(1.1,256, 4, 9)); 432 | } 433 | 434 | [Test] 435 | public void Blob() 436 | { 437 | Check( 438 | new byte[] 439 | { 440 | 3, 1, 2, 3, 3, 100, 1 441 | }, 442 | FlexBuffer.SingleValue(new byte[]{1, 2, 3})); 443 | 444 | var buffer = new byte[1001]; 445 | for (int i = 0; i <= 1000; i++) 446 | { 447 | buffer[i] = 5; 448 | } 449 | 450 | var expected = new byte[1008]; 451 | expected[0] = 233; 452 | expected[1] = 3; 453 | Buffer.BlockCopy(buffer, 0, expected, 2, 1000); 454 | expected[1002] = 5; 455 | expected[1003] = 0; 456 | expected[1004] = 234; 457 | expected[1005] = 3; 458 | expected[1006] = 101; 459 | expected[1007] = 2; 460 | Check( 461 | expected, 462 | FlexBuffer.SingleValue(buffer)); 463 | } 464 | 465 | [Test] 466 | public void LongStringArray() 467 | { 468 | var s1 = new StringBuilder(); 469 | for (int i = 0; i < 260; i++) 470 | { 471 | s1.Append("a"); 472 | } 473 | var s2 = new StringBuilder(); 474 | for (int i = 0; i < 260000; i++) 475 | { 476 | s2.Append("b"); 477 | } 478 | 479 | var list = new List(2) {s1.ToString(), s2.ToString()}; 480 | 481 | var bytes = FlexBuffer.From(list); 482 | 483 | var flx = FlxValue.FromBytes(bytes); 484 | 485 | Assert.AreEqual(2, flx.AsVector.Length); 486 | Assert.AreEqual(s1.ToString(), flx[0].AsString); 487 | Assert.AreEqual(s2.ToString(), flx[1].AsString); 488 | 489 | } 490 | 491 | [Test] 492 | public void BiggerStringArray() 493 | { 494 | var list = new List(); 495 | for (var i = 0; i < 2600; i++) 496 | { 497 | list.Add("abc"); 498 | } 499 | 500 | var bytes = FlexBuffer.From(list); 501 | 502 | var flx = FlxValue.FromBytes(bytes); 503 | 504 | Assert.AreEqual(2600, flx.AsVector.Length); 505 | for (var i = 0; i < 2600; i++) 506 | { 507 | Assert.AreEqual("abc", flx[i].AsString); 508 | } 509 | } 510 | 511 | [Test] 512 | public void BiggerBoolArray() 513 | { 514 | var list = new List(); 515 | for (var i = 0; i < 2600; i++) 516 | { 517 | list.Add(i%3 == 0); 518 | } 519 | 520 | var bytes = FlexBuffer.From(list); 521 | 522 | var flx = FlxValue.FromBytes(bytes); 523 | 524 | Assert.AreEqual(2600, flx.AsVector.Length); 525 | for (var i = 0; i < 2600; i++) 526 | { 527 | Assert.AreEqual(list[i], flx[i].AsBool); 528 | } 529 | } 530 | 531 | [Test] 532 | public void BiggerDictionary() 533 | { 534 | var dict = new Dictionary(); 535 | for (var i = 0; i < 2600; i++) 536 | { 537 | dict[i.ToString()] = i; 538 | } 539 | 540 | var bytes = FlexBuffer.From(dict); 541 | 542 | var flx = FlxValue.FromBytes(bytes); 543 | 544 | Assert.AreEqual(2600, flx.AsMap.Length); 545 | for (var i = 0; i < 2600; i++) 546 | { 547 | Assert.AreEqual(i, flx[i.ToString()].AsLong); 548 | } 549 | } 550 | 551 | [Test] 552 | public void DictionaryWithCapitalAndNonCapitalKeyValues() 553 | { 554 | Dictionary dict = new Dictionary(); 555 | dict.Add("aieorjhioa", "aerhj"); 556 | dict.Add("e98rh8d9i3qaktgr", "eaihjor"); 557 | dict.Add("T", "toeks"); 558 | 559 | byte[] testBytes = FlexBuffer.From(dict); 560 | FlxMap map = FlxValue.FromBytes(testBytes).AsMap; 561 | 562 | 563 | Assert.AreEqual("aerhj", map["aieorjhioa"].AsString); 564 | Assert.AreEqual("eaihjor", map["e98rh8d9i3qaktgr"].AsString); 565 | Assert.AreEqual("toeks", map["T"].AsString); 566 | } 567 | 568 | private void Check(byte[] expected, byte[] actual) 569 | { 570 | Assert.AreEqual(expected, actual, string.Join(", ", actual)); 571 | } 572 | } 573 | } -------------------------------------------------------------------------------- /FlexBuffersCSharpTests/FlexBuffersCSharpTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {16A313A0-E208-4A82-8464-628C008E77CB} 8 | {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 9 | Library 10 | Properties 11 | FlexBuffersCSharpTests 12 | FlexBuffersCSharpTests 13 | v4.7.1 14 | 512 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | ..\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | {413de2e2-0412-4eee-ab96-bb4ef5eecae1} 57 | FlexBuffers 58 | 59 | 60 | 61 | 68 | -------------------------------------------------------------------------------- /FlexBuffersCSharpTests/FlxValueTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Security.Cryptography; 5 | using FlexBuffers; 6 | using NUnit.Framework; 7 | 8 | namespace FlexBuffersCSharpTests 9 | { 10 | [TestFixture] 11 | public class FlxValueTests 12 | { 13 | [Test] 14 | public void Null() 15 | { 16 | var bytes = FlexBuffer.Null(); 17 | var flx = FlxValue.FromBytes(bytes); 18 | Assert.AreEqual(true, flx.IsNull); 19 | } 20 | 21 | [Test] 22 | public void Long() 23 | { 24 | CheckLong(0); 25 | CheckLong(1); 26 | CheckLong(-5); 27 | CheckLong(byte.MaxValue); 28 | CheckLong(byte.MinValue); 29 | CheckLong(short.MaxValue); 30 | CheckLong(short.MinValue); 31 | CheckLong(int.MaxValue); 32 | CheckLong(int.MinValue); 33 | CheckLong(long.MaxValue); 34 | CheckLong(long.MinValue); 35 | } 36 | 37 | [Test] 38 | public void ULong() 39 | { 40 | CheckULong(byte.MaxValue); 41 | CheckULong(byte.MinValue); 42 | CheckULong(ushort.MaxValue); 43 | CheckULong(ushort.MinValue); 44 | CheckULong(uint.MaxValue); 45 | CheckULong(uint.MinValue); 46 | CheckULong(ulong.MaxValue); 47 | CheckULong(ulong.MinValue); 48 | CheckLongAsULong(1); 49 | CheckLongAsULong(sbyte.MaxValue); 50 | CheckLongAsULong(short.MaxValue); 51 | CheckLongAsULong(int.MaxValue); 52 | CheckLongAsULong(long.MaxValue); 53 | } 54 | 55 | [Test] 56 | public void Double() 57 | { 58 | CheckDouble(0); 59 | CheckDouble(-5); 60 | CheckDouble(-5.1); 61 | CheckDouble(0.1); 62 | CheckDouble(5.5); 63 | CheckDouble(5.25); 64 | CheckLongAsDouble(long.MaxValue); 65 | CheckLongAsDouble(long.MinValue); 66 | CheckULongAsDouble(ulong.MaxValue); 67 | } 68 | 69 | [Test] 70 | public void Bool() 71 | { 72 | CheckBool(true); 73 | CheckBool(false); 74 | CheckLongAsBool(0); 75 | CheckLongAsBool(1); 76 | CheckLongAsBool(-1); 77 | CheckULongAsBool(1); 78 | CheckULongAsBool(5); 79 | CheckULongAsBool(0); 80 | } 81 | 82 | [Test] 83 | public void String() 84 | { 85 | CheckString(""); 86 | CheckString("Maxim"); 87 | CheckString("Max"); 88 | CheckString("Alex"); 89 | CheckString("Hi 😍 🤓 🥳"); 90 | } 91 | 92 | [Test] 93 | public void Blob() 94 | { 95 | var value = new byte[] {1, 2, 3, 4}; 96 | var bytes = FlexBuffer.SingleValue(value); 97 | var flx = FlxValue.FromBytes(bytes); 98 | Assert.AreEqual(value, flx.AsBlob); 99 | } 100 | 101 | [Test] 102 | public void BlobToJson() 103 | { 104 | var value = new byte[] {1, 2, 3, 4}; 105 | var bytes = FlexBuffer.SingleValue(value); 106 | var flx = FlxValue.FromBytes(bytes); 107 | Assert.AreEqual("\"AQIDBA==\"", flx.ToJson); 108 | Assert.AreEqual(value, Convert.FromBase64String("AQIDBA==")); 109 | } 110 | 111 | [Test] 112 | public void IntArray() 113 | { 114 | var value = new[] {1, 2, 3, -1}; 115 | var bytes = FlexBuffer.From(value); 116 | var flx = FlxValue.FromBytes(bytes); 117 | Assert.AreEqual(value.Length, flx.AsVector.Length); 118 | for (var i = 0; i < value.Length; i++) 119 | { 120 | Assert.AreEqual(value[i], flx[i].AsLong, $"Not equal at index {i}"); 121 | } 122 | } 123 | 124 | [Test] 125 | public void DoubleArray() 126 | { 127 | var value = new[] {1.1, 2.5, 3, -1}; 128 | var bytes = FlexBuffer.From(value); 129 | var flx = FlxValue.FromBytes(bytes); 130 | Assert.AreEqual(value.Length, flx.AsVector.Length); 131 | for (var i = 0; i < value.Length; i++) 132 | { 133 | var v = flx[i].AsDouble; 134 | Assert.AreEqual(value[i], v, $"Not equal at index {i}"); 135 | } 136 | } 137 | 138 | [Test] 139 | public void BoolArray() 140 | { 141 | var value = new[] {true, false, true, true}; 142 | var bytes = FlexBuffer.From(value); 143 | var flx = FlxValue.FromBytes(bytes); 144 | Assert.AreEqual(value.Length, flx.AsVector.Length); 145 | for (var i = 0; i < value.Length; i++) 146 | { 147 | var v = flx[i].AsBool; 148 | Assert.AreEqual(value[i], v, $"Not equal at index {i}"); 149 | } 150 | } 151 | 152 | [Test] 153 | public void StringArray() 154 | { 155 | var value = new[] {"Max", "Maxim", "Alex", "Hi 😂🤣😍🤪"}; 156 | var bytes = FlexBuffer.From(value); 157 | var flx = FlxValue.FromBytes(bytes); 158 | Assert.AreEqual(value.Length, flx.AsVector.Length); 159 | for (var i = 0; i < value.Length; i++) 160 | { 161 | var v = flx[i].AsString; 162 | Assert.AreEqual(value[i], v, $"Not equal at index {i}"); 163 | } 164 | } 165 | 166 | [Test] 167 | public void MixedArray() 168 | { 169 | var value = new object[] {"Max", 1, 0.1, 0.5, true, int.MinValue, ulong.MaxValue, "Hi 😂🤣😍🤪", null}; 170 | var bytes = FlexBuffer.From(value); 171 | var flx = FlxValue.FromBytes(bytes); 172 | Assert.AreEqual(value.Length, flx.AsVector.Length); 173 | Assert.AreEqual(value[0], flx[0].AsString); 174 | Assert.AreEqual(value[1], flx[1].AsLong); 175 | Assert.AreEqual(value[2], flx[2].AsDouble); 176 | Assert.AreEqual(value[3], flx[3].AsDouble); 177 | Assert.AreEqual(value[4], flx[4].AsBool); 178 | Assert.AreEqual(value[5], flx[5].AsLong); 179 | Assert.AreEqual(value[6], flx[6].AsULong); 180 | Assert.AreEqual(value[7], flx[7].AsString); 181 | Assert.AreEqual(true, flx[8].IsNull); 182 | } 183 | 184 | [Test] 185 | public void SimpleMap() 186 | { 187 | var value = new Dictionary() 188 | { 189 | {"a", 12}, 190 | {"b", 45} 191 | }; 192 | var bytes = FlexBuffer.From(value); 193 | var flx = FlxValue.FromBytes(bytes); 194 | Assert.AreEqual(value.Count, flx.AsMap.Length); 195 | Assert.AreEqual(value["a"], flx["a"].AsLong); 196 | Assert.AreEqual(value["b"], flx["b"].AsLong); 197 | } 198 | 199 | [Test] 200 | public void ComplexMap() 201 | { 202 | var value = new Dictionary() 203 | { 204 | {"age", 35}, 205 | {"flags", new[]{true, false, true, true}}, 206 | {"weight", 72.5}, 207 | {"name", "Maxim"}, 208 | {"address", new Dictionary() 209 | { 210 | {"city", "Bla"}, 211 | {"zip", "12345"}, 212 | {"countryCode", "XX"}, 213 | }}, 214 | }; 215 | var bytes = FlexBuffer.From(value); 216 | var flx = FlxValue.FromBytes(bytes); 217 | Assert.AreEqual(value.Count, flx.AsMap.Length); 218 | 219 | Assert.AreEqual(35, flx["age"].AsLong); 220 | Assert.AreEqual(72.5, flx["weight"].AsDouble); 221 | Assert.AreEqual("Maxim", flx["name"].AsString); 222 | 223 | Assert.AreEqual(4, flx["flags"].AsVector.Length); 224 | Assert.AreEqual(true, flx["flags"][0].AsBool); 225 | Assert.AreEqual(false, flx["flags"][1].AsBool); 226 | Assert.AreEqual(true, flx["flags"][2].AsBool); 227 | Assert.AreEqual(true, flx["flags"][3].AsBool); 228 | 229 | Assert.AreEqual(3, flx["address"].AsMap.Length); 230 | Assert.AreEqual("Bla", flx["address"]["city"].AsString); 231 | Assert.AreEqual("12345", flx["address"]["zip"].AsString); 232 | Assert.AreEqual("XX", flx["address"]["countryCode"].AsString); 233 | } 234 | 235 | [Test] 236 | public void ComplexMapToJson() 237 | { 238 | var value = new Dictionary() 239 | { 240 | {"age", 35}, 241 | {"flags", new[]{true, false, true, true}}, 242 | {"weight", 72.5}, 243 | {"name", "Maxim"}, 244 | {"address", new Dictionary() 245 | { 246 | {"city", "Bla"}, 247 | {"zip", "12345"}, 248 | {"countryCode", "XX"}, 249 | }}, 250 | {"something", null} 251 | }; 252 | var bytes = FlexBuffer.From(value); 253 | var flx = FlxValue.FromBytes(bytes); 254 | const string expected = "{\"address\":{\"city\":\"Bla\",\"countryCode\":\"XX\",\"zip\":\"12345\"},\"age\":35,\"flags\":[true,false,true,true],\"name\":\"Maxim\",\"something\":null,\"weight\":72.5}"; 255 | var json = flx.ToJson; 256 | Assert.AreEqual(expected, json); 257 | } 258 | 259 | [Test] 260 | public void Long2() 261 | { 262 | var bytes = FlexBuffer.SingleValue(1, 2); 263 | var flx = FlxValue.FromBytes(bytes); 264 | Assert.AreEqual(2, flx.AsVector.Length); 265 | Assert.AreEqual(flx[0].AsLong, 1); 266 | Assert.AreEqual(flx[1].AsLong, 2); 267 | 268 | bytes = FlexBuffer.SingleValue(1, 256); 269 | flx = FlxValue.FromBytes(bytes); 270 | Assert.AreEqual(2, flx.AsVector.Length); 271 | Assert.AreEqual(flx[0].AsLong, 1); 272 | Assert.AreEqual(flx[1].AsLong, 256); 273 | 274 | bytes = FlexBuffer.SingleValue(1, long.MaxValue); 275 | flx = FlxValue.FromBytes(bytes); 276 | Assert.AreEqual(2, flx.AsVector.Length); 277 | Assert.AreEqual(flx[0].AsLong, 1); 278 | Assert.AreEqual(flx[1].AsLong, long.MaxValue); 279 | } 280 | 281 | [Test] 282 | public void ULong2() 283 | { 284 | var bytes = FlexBuffer.SingleValue(1UL, 2); 285 | var flx = FlxValue.FromBytes(bytes); 286 | Assert.AreEqual(2, flx.AsVector.Length); 287 | Assert.AreEqual(flx[0].AsULong, 1); 288 | Assert.AreEqual(flx[1].AsULong, 2); 289 | 290 | bytes = FlexBuffer.SingleValue(1, 256); 291 | flx = FlxValue.FromBytes(bytes); 292 | Assert.AreEqual(2, flx.AsVector.Length); 293 | Assert.AreEqual(flx[0].AsULong, 1); 294 | Assert.AreEqual(flx[1].AsULong, 256); 295 | 296 | bytes = FlexBuffer.SingleValue(1, ulong.MaxValue); 297 | flx = FlxValue.FromBytes(bytes); 298 | Assert.AreEqual(2, flx.AsVector.Length); 299 | Assert.AreEqual(flx[0].AsULong, 1); 300 | Assert.AreEqual(flx[1].AsULong, ulong.MaxValue); 301 | } 302 | 303 | [Test] 304 | public void Double2() 305 | { 306 | var bytes = FlexBuffer.SingleValue(1.1f, 2); 307 | var flx = FlxValue.FromBytes(bytes); 308 | Assert.AreEqual(2, flx.AsVector.Length); 309 | Assert.AreEqual(flx[0].AsDouble, 1.1f); 310 | Assert.AreEqual(flx[1].AsDouble, 2); 311 | 312 | bytes = FlexBuffer.SingleValue(1.1, 256); 313 | flx = FlxValue.FromBytes(bytes); 314 | Assert.AreEqual(2, flx.AsVector.Length); 315 | Assert.AreEqual(flx[0].AsDouble, 1.1); 316 | Assert.AreEqual(flx[1].AsDouble, 256); 317 | } 318 | 319 | [Test] 320 | public void Double3() 321 | { 322 | var bytes = FlexBuffer.SingleValue(1.1f, 2, 3.3f); 323 | var flx = FlxValue.FromBytes(bytes); 324 | Assert.AreEqual(3, flx.AsVector.Length); 325 | Assert.AreEqual(flx[0].AsDouble, 1.1f); 326 | Assert.AreEqual(flx[1].AsDouble, 2); 327 | Assert.AreEqual(flx[2].AsDouble, 3.3f); 328 | 329 | bytes = FlexBuffer.SingleValue(1.1, 256, 3.3); 330 | flx = FlxValue.FromBytes(bytes); 331 | Assert.AreEqual(3, flx.AsVector.Length); 332 | Assert.AreEqual(flx[0].AsDouble, 1.1); 333 | Assert.AreEqual(flx[1].AsDouble, 256); 334 | Assert.AreEqual(flx[2].AsDouble, 3.3); 335 | } 336 | 337 | [Test] 338 | public void Double4() 339 | { 340 | var bytes = FlexBuffer.SingleValue(1.1f, 2, 3.3f, 0.5); 341 | var flx = FlxValue.FromBytes(bytes); 342 | Assert.AreEqual(4, flx.AsVector.Length); 343 | Assert.AreEqual(flx[0].AsDouble, 1.1f); 344 | Assert.AreEqual(flx[1].AsDouble, 2); 345 | Assert.AreEqual(flx[2].AsDouble, 3.3f); 346 | Assert.AreEqual(flx[3].AsDouble, 0.5); 347 | 348 | bytes = FlexBuffer.SingleValue(1.1, 256, 3.3, 0.5); 349 | flx = FlxValue.FromBytes(bytes); 350 | Assert.AreEqual(4, flx.AsVector.Length); 351 | Assert.AreEqual(flx[0].AsDouble, 1.1); 352 | Assert.AreEqual(flx[1].AsDouble, 256); 353 | Assert.AreEqual(flx[2].AsDouble, 3.3); 354 | Assert.AreEqual(flx[3].AsDouble, 0.5); 355 | } 356 | 357 | [Test] 358 | public void IterateOverVector() 359 | { 360 | var ints = new[] {1, 2, 3}; 361 | var bytes = FlexBuffer.From(ints); 362 | var flx = FlxValue.FromBytes(bytes); 363 | var i = 0; 364 | foreach (var value in flx.AsVector) 365 | { 366 | Assert.AreEqual(ints[i], value.AsLong); 367 | i++; 368 | } 369 | Assert.AreEqual(3, i); 370 | } 371 | 372 | [Test] 373 | public void IterateOverMap() 374 | { 375 | var dict = new Dictionary() 376 | { 377 | {"a", 1}, 378 | {"b", 2}, 379 | {"c", 3}, 380 | }; 381 | var bytes = FlexBuffer.From(dict); 382 | var flx = FlxValue.FromBytes(bytes); 383 | var i = 0; 384 | foreach (var value in flx.AsMap) 385 | { 386 | Assert.AreEqual(dict[value.Key], value.Value.AsLong); 387 | i++; 388 | } 389 | Assert.AreEqual(3, i); 390 | } 391 | 392 | [Test] 393 | public void GetValueIndex() 394 | { 395 | var dict = new Dictionary() 396 | { 397 | {"a", 1}, 398 | {"b", 2}, 399 | {"c", 3}, 400 | }; 401 | var bytes = FlexBuffer.From(dict); 402 | var flx = FlxValue.FromBytes(bytes); 403 | var map = flx.AsMap; 404 | Assert.AreEqual(3, map.Length); 405 | Assert.AreEqual(0, map.KeyIndex("a")); 406 | Assert.AreEqual(1, map.KeyIndex("b")); 407 | Assert.AreEqual(2, map.KeyIndex("c")); 408 | Assert.AreEqual(-1, map.KeyIndex("d")); 409 | Assert.AreEqual(1, map.ValueByIndex(0).AsLong); 410 | 411 | } 412 | 413 | [Test] 414 | public void TestPrettyJson2DVector() 415 | { 416 | var list = new List> 417 | { 418 | new List(){1, 2, 3}, 419 | new List(){5, 6}, 420 | new List(){7, 8, 9, 10}, 421 | }; 422 | 423 | var expected = @"[ 424 | [ 425 | 1, 426 | 2, 427 | 3 428 | ], 429 | [ 430 | 5, 431 | 6 432 | ], 433 | [ 434 | 7, 435 | 8, 436 | 9, 437 | 10 438 | ] 439 | ]".Replace("\r", ""); 440 | var bytes = FlexBuffer.From(list); 441 | var flx = FlxValue.FromBytes(bytes); 442 | Assert.AreEqual(expected, flx.ToPrettyJson()); 443 | } 444 | 445 | [Test] 446 | public void TestPrettyJsonMap() 447 | { 448 | var dict = new Dictionary() 449 | { 450 | {"a", 1}, 451 | {"b", 2}, 452 | {"c", 3}, 453 | {"d", new []{1, 2, 3}} 454 | }; 455 | 456 | var expected = @"{ 457 | ""a"" : 1, 458 | ""b"" : 2, 459 | ""c"" : 3, 460 | ""d"" : [ 461 | 1, 462 | 2, 463 | 3 464 | ] 465 | }".Replace("\r", ""); 466 | var bytes = FlexBuffer.From(dict); 467 | var flx = FlxValue.FromBytes(bytes); 468 | Assert.AreEqual(expected, flx.ToPrettyJson()); 469 | } 470 | 471 | [Test] 472 | public void TestOffsetAndLengthAreOfTypeULong() 473 | { 474 | var json = @"{""channels_in"":64,""dilation_height_factor"":1,""dilation_width_factor"":1,""fused_activation_function"":1,""pad_values"":1,""padding"":0,""stride_height"":1,""stride_width"":1}"; 475 | var flx = FlxValue.FromBytes(new byte[] {99, 104, 97, 110, 110, 101, 108, 115, 95, 105, 110, 0, 100, 105, 108, 97, 116, 105, 111, 110, 95, 104, 101, 105, 103, 104, 116, 95, 102, 97, 99, 116, 111, 114, 0, 100, 105, 108, 97, 116, 105, 111, 110, 95, 119, 105, 100, 116, 104, 95, 102, 97, 99, 116, 111, 114, 0, 102, 117, 115, 101, 100, 95, 97, 99, 116, 105, 118, 97, 116, 105, 111, 110, 95, 102, 117, 110, 99, 116, 105, 111, 110, 0, 112, 97, 100, 95, 118, 97, 108, 117, 101, 115, 0, 112, 97, 100, 100, 105, 110, 103, 0, 115, 116, 114, 105, 100, 101, 95, 104, 101, 105, 103, 104, 116, 0, 115, 116, 114, 105, 100, 101, 95, 119, 105, 100, 116, 104, 0, 8, 130, 119, 97, 76, 51, 41, 34, 21, 8, 1, 8, 64, 1, 1, 1, 1, 0, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 16, 36, 1}); 476 | Assert.AreEqual(json, flx.ToJson); 477 | } 478 | 479 | private void CheckLong(long value) 480 | { 481 | var bytes = FlexBuffer.SingleValue(value); 482 | var flx = FlxValue.FromBytes(bytes); 483 | Assert.AreEqual(value, flx.AsLong); 484 | } 485 | 486 | private void CheckULong(ulong value) 487 | { 488 | var bytes = FlexBuffer.SingleValue(value); 489 | var flx = FlxValue.FromBytes(bytes); 490 | Assert.AreEqual(value, flx.AsULong); 491 | } 492 | 493 | private void CheckLongAsULong(long value) 494 | { 495 | var bytes = FlexBuffer.SingleValue(value); 496 | var flx = FlxValue.FromBytes(bytes); 497 | Assert.AreEqual(value, flx.AsULong); 498 | } 499 | 500 | private void CheckLongAsDouble(long value) 501 | { 502 | var bytes = FlexBuffer.SingleValue(value); 503 | var flx = FlxValue.FromBytes(bytes); 504 | Assert.AreEqual(value, flx.AsDouble); 505 | } 506 | 507 | private void CheckULongAsDouble(ulong value) 508 | { 509 | var bytes = FlexBuffer.SingleValue(value); 510 | var flx = FlxValue.FromBytes(bytes); 511 | Assert.AreEqual(value, flx.AsDouble); 512 | } 513 | 514 | private void CheckDouble(double value) 515 | { 516 | var bytes = FlexBuffer.SingleValue(value); 517 | var flx = FlxValue.FromBytes(bytes); 518 | Assert.AreEqual(value, flx.AsDouble); 519 | } 520 | 521 | private void CheckBool(bool value) 522 | { 523 | var bytes = FlexBuffer.SingleValue(value); 524 | var flx = FlxValue.FromBytes(bytes); 525 | Assert.AreEqual(value, flx.AsBool); 526 | } 527 | 528 | private void CheckLongAsBool(long value) 529 | { 530 | var bytes = FlexBuffer.SingleValue(value); 531 | var flx = FlxValue.FromBytes(bytes); 532 | Assert.AreEqual(value != 0, flx.AsBool); 533 | } 534 | 535 | private void CheckULongAsBool(ulong value) 536 | { 537 | var bytes = FlexBuffer.SingleValue(value); 538 | var flx = FlxValue.FromBytes(bytes); 539 | Assert.AreEqual(value != 0, flx.AsBool); 540 | } 541 | 542 | private void CheckString(string value) 543 | { 544 | var bytes = FlexBuffer.SingleValue(value); 545 | var flx = FlxValue.FromBytes(bytes); 546 | Assert.AreEqual(value, flx.AsString); 547 | } 548 | } 549 | } -------------------------------------------------------------------------------- /FlexBuffersCSharpTests/JsonToFlexBufferConverterTests.cs: -------------------------------------------------------------------------------- 1 | using FlexBuffers; 2 | using NUnit.Framework; 3 | 4 | namespace FlexBuffersCSharpTests 5 | { 6 | [TestFixture] 7 | public class JsonToFlexBufferConverterTests 8 | { 9 | [Test] 10 | public void Null() 11 | { 12 | var buffer = JsonToFlexBufferConverter.Convert("null"); 13 | var flx = FlxValue.FromBytes(buffer); 14 | Assert.AreEqual(true, flx.IsNull); 15 | } 16 | 17 | [Test] 18 | public void Int() 19 | { 20 | var buffer = JsonToFlexBufferConverter.Convert("3456"); 21 | var flx = FlxValue.FromBytes(buffer); 22 | Assert.AreEqual(3456, flx.AsLong); 23 | } 24 | 25 | [Test] 26 | public void NegativeInt() 27 | { 28 | var buffer = JsonToFlexBufferConverter.Convert("-3456"); 29 | var flx = FlxValue.FromBytes(buffer); 30 | Assert.AreEqual(-3456, flx.AsLong); 31 | } 32 | 33 | [Test] 34 | public void Float() 35 | { 36 | var buffer = JsonToFlexBufferConverter.Convert("34.56"); 37 | var flx = FlxValue.FromBytes(buffer); 38 | Assert.AreEqual(34.56, flx.AsDouble); 39 | } 40 | 41 | [Test] 42 | public void NegativeFloat() 43 | { 44 | var buffer = JsonToFlexBufferConverter.Convert("-34.56"); 45 | var flx = FlxValue.FromBytes(buffer); 46 | Assert.AreEqual(-34.56, flx.AsDouble); 47 | } 48 | 49 | [Test] 50 | public void MixedVector() 51 | { 52 | var buffer = JsonToFlexBufferConverter.Convert("[null, true, false, \"hello 🙀\", -34, 6.1]"); 53 | var flx = FlxValue.FromBytes(buffer); 54 | Assert.AreEqual(6, flx.AsVector.Length); 55 | Assert.AreEqual(true, flx[0].IsNull); 56 | Assert.AreEqual(true, flx[1].AsBool); 57 | Assert.AreEqual(false, flx[2].AsBool); 58 | Assert.AreEqual("hello 🙀", flx[3].AsString); 59 | Assert.AreEqual(-34, flx[4].AsLong); 60 | Assert.AreEqual(6.1, flx[5].AsDouble); 61 | } 62 | 63 | [Test] 64 | public void EmptyVector() 65 | { 66 | var buffer = JsonToFlexBufferConverter.Convert("[]"); 67 | var flx = FlxValue.FromBytes(buffer); 68 | Assert.AreEqual(0, flx.AsVector.Length); 69 | } 70 | 71 | [Test] 72 | public void EmptyMap() 73 | { 74 | var buffer = JsonToFlexBufferConverter.Convert("{}"); 75 | var flx = FlxValue.FromBytes(buffer); 76 | Assert.AreEqual(0, flx.AsMap.Length); 77 | } 78 | 79 | [Test] 80 | public void OneKeyMap() 81 | { 82 | var buffer = JsonToFlexBufferConverter.Convert("{\"\":1}"); 83 | var flx = FlxValue.FromBytes(buffer); 84 | Assert.AreEqual(1, flx.AsMap.Length); 85 | Assert.AreEqual(1, flx[""].AsLong); 86 | 87 | } 88 | 89 | [Test] 90 | public void TwoKeysMap() 91 | { 92 | var buffer = JsonToFlexBufferConverter.Convert("{\"a\":1, \"b\":2}"); 93 | var flx = FlxValue.FromBytes(buffer); 94 | Assert.AreEqual(2, flx.AsMap.Length); 95 | Assert.AreEqual(1, flx["a"].AsLong); 96 | Assert.AreEqual(2, flx["b"].AsLong); 97 | } 98 | 99 | [Test] 100 | public void TwoKeysMapUnsorted() 101 | { 102 | var buffer = JsonToFlexBufferConverter.Convert("{\"b\":2,\"a\":1}"); 103 | var flx = FlxValue.FromBytes(buffer); 104 | Assert.AreEqual(2, flx.AsMap.Length); 105 | Assert.AreEqual(1, flx["a"].AsLong); 106 | Assert.AreEqual(2, flx["b"].AsLong); 107 | } 108 | 109 | [Test] 110 | public void ComplexMap() 111 | { 112 | 113 | const string json = @" 114 | { 115 | ""age"": 35, 116 | ""weight"": 72.5, 117 | ""name"": ""Maxim"", 118 | ""flags"": [true, false, true, true], 119 | ""something"": null, 120 | ""address"": { 121 | ""city"": ""Bla"", 122 | ""zip"": ""12345"", 123 | ""countryCode"": ""XX"" 124 | } 125 | } 126 | "; 127 | var bytes = JsonToFlexBufferConverter.Convert(json); 128 | var flx = FlxValue.FromBytes(bytes); 129 | Assert.AreEqual(6, flx.AsMap.Length); 130 | 131 | Assert.AreEqual(35, flx["age"].AsLong); 132 | Assert.AreEqual(72.5, flx["weight"].AsDouble); 133 | Assert.AreEqual("Maxim", flx["name"].AsString); 134 | Assert.AreEqual(true, flx["something"].IsNull); 135 | 136 | Assert.AreEqual(4, flx["flags"].AsVector.Length); 137 | Assert.AreEqual(true, flx["flags"][0].AsBool); 138 | Assert.AreEqual(false, flx["flags"][1].AsBool); 139 | Assert.AreEqual(true, flx["flags"][2].AsBool); 140 | Assert.AreEqual(true, flx["flags"][3].AsBool); 141 | 142 | Assert.AreEqual(3, flx["address"].AsMap.Length); 143 | Assert.AreEqual("Bla", flx["address"]["city"].AsString); 144 | Assert.AreEqual("12345", flx["address"]["zip"].AsString); 145 | Assert.AreEqual("XX", flx["address"]["countryCode"].AsString); 146 | } 147 | 148 | [Test] 149 | public void VectorOfMaps() 150 | { 151 | 152 | const string json = @" 153 | [ 154 | {""name"": ""Max"", ""age"": 38}, 155 | {""name"": ""Maxim"", ""age"": 35}, 156 | {""age"": 18, ""name"": ""Alex""} 157 | ] 158 | "; 159 | var bytes = JsonToFlexBufferConverter.Convert(json); 160 | var flx = FlxValue.FromBytes(bytes); 161 | 162 | Assert.AreEqual(3, flx.AsVector.Length); 163 | 164 | Assert.AreEqual(2, flx[0].AsMap.Length); 165 | Assert.AreEqual("Max", flx[0]["name"].AsString); 166 | Assert.AreEqual(38, flx[0]["age"].AsLong); 167 | 168 | Assert.AreEqual(2, flx[1].AsMap.Length); 169 | Assert.AreEqual("Maxim", flx[1]["name"].AsString); 170 | Assert.AreEqual(35, flx[1]["age"].AsLong); 171 | 172 | Assert.AreEqual(2, flx[2].AsMap.Length); 173 | Assert.AreEqual("Alex", flx[2]["name"].AsString); 174 | Assert.AreEqual(18, flx[2]["age"].AsLong); 175 | } 176 | 177 | [Test] 178 | public void TestOffsetAndLengthAreOfTypeULong() 179 | { 180 | var json = @"{""channels_in"":64,""dilation_height_factor"":1,""dilation_width_factor"":1,""fused_activation_function"":1,""pad_values"":1,""padding"":0,""stride_height"":1,""stride_width"":1}"; 181 | var bytes = JsonToFlexBufferConverter.Convert(json); 182 | var expectedBytes = new byte[] {99, 104, 97, 110, 110, 101, 108, 115, 95, 105, 110, 0, 100, 105, 108, 97, 116, 105, 111, 110, 95, 104, 101, 105, 103, 104, 116, 95, 102, 97, 99, 116, 111, 114, 0, 100, 105, 108, 97, 116, 105, 111, 110, 95, 119, 105, 100, 116, 104, 95, 102, 97, 99, 116, 111, 114, 0, 102, 117, 115, 101, 100, 95, 97, 99, 116, 105, 118, 97, 116, 105, 111, 110, 95, 102, 117, 110, 99, 116, 105, 111, 110, 0, 112, 97, 100, 95, 118, 97, 108, 117, 101, 115, 0, 112, 97, 100, 100, 105, 110, 103, 0, 115, 116, 114, 105, 100, 101, 95, 104, 101, 105, 103, 104, 116, 0, 115, 116, 114, 105, 100, 101, 95, 119, 105, 100, 116, 104, 0, 8, 130, 119, 97, 76, 51, 41, 34, 21, 8, 1, 8, 64, 1, 1, 1, 1, 0, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 16, 36, 1}; 183 | Assert.AreEqual(expectedBytes, bytes); 184 | } 185 | } 186 | } -------------------------------------------------------------------------------- /FlexBuffersCSharpTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("FlexBuffersCSharpTests")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("FlexBuffersCSharpTests")] 12 | [assembly: AssemblyCopyright("Copyright © 2019")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("16A313A0-E208-4A82-8464-628C008E77CB")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /FlexBuffersCSharpTests/XmlToFlexBufferConverterTests.cs: -------------------------------------------------------------------------------- 1 | using FlexBuffers; 2 | using NUnit.Framework; 3 | 4 | namespace FlexBuffersCSharpTests 5 | { 6 | [TestFixture] 7 | public class XmlToFlexBufferConverterTests 8 | { 9 | [Test] 10 | public void SingleTagWithAttributes() 11 | { 12 | var bytes = XmlToFlexBufferConverter.Convert(""); 13 | 14 | var flx = FlxValue.FromBytes(bytes); 15 | 16 | Assert.AreEqual(3, flx.AsMap.Length); 17 | Assert.AreEqual("tag1", flx["tagName"].AsString); 18 | Assert.AreEqual("123", flx["a"].AsString); 19 | Assert.AreEqual("321", flx["b"].AsString); 20 | } 21 | 22 | [Test] 23 | public void SingleOpenCloseTagWithAttributes() 24 | { 25 | var bytes = XmlToFlexBufferConverter.Convert(""); 26 | 27 | var flx = FlxValue.FromBytes(bytes); 28 | 29 | Assert.AreEqual(3, flx.AsMap.Length); 30 | Assert.AreEqual("tag1", flx["tagName"].AsString); 31 | Assert.AreEqual("123", flx["a"].AsString); 32 | Assert.AreEqual("321", flx["b"].AsString); 33 | } 34 | 35 | [Test] 36 | public void SingleOpenCloseTagWithAttributesAndText() 37 | { 38 | var bytes = XmlToFlexBufferConverter.Convert("hello"); 39 | 40 | var flx = FlxValue.FromBytes(bytes); 41 | 42 | Assert.AreEqual(4, flx.AsMap.Length); 43 | Assert.AreEqual("tag1", flx["tagName"].AsString); 44 | Assert.AreEqual("123", flx["a"].AsString); 45 | Assert.AreEqual("321", flx["b"].AsString); 46 | 47 | Assert.AreEqual(1, flx["children"].AsVector.Length); 48 | Assert.AreEqual("hello", flx["children"][0].AsString); 49 | } 50 | 51 | [Test] 52 | public void SingleOpenCloseTagWithAttributesAndTextSplitByTag() 53 | { 54 | var bytes = XmlToFlexBufferConverter.Convert("hello
Maxim
"); 55 | 56 | var flx = FlxValue.FromBytes(bytes); 57 | 58 | Assert.AreEqual(4, flx.AsMap.Length); 59 | Assert.AreEqual("tag1", flx["tagName"].AsString); 60 | Assert.AreEqual("123", flx["a"].AsString); 61 | Assert.AreEqual("321", flx["b"].AsString); 62 | 63 | Assert.AreEqual(3, flx["children"].AsVector.Length); 64 | Assert.AreEqual("hello ", flx["children"][0].AsString); 65 | Assert.AreEqual(1, flx["children"][1].AsMap.Length); 66 | Assert.AreEqual("br", flx["children"][1]["tagName"].AsString); 67 | Assert.AreEqual(" Maxim", flx["children"][2].AsString); 68 | } 69 | 70 | [Test] 71 | public void ComplexXml() 72 | { 73 | var xml = @" 74 | 75 | ]> 76 | 77 | Test with an entity: &number; 78 | Test with a child element stuff 79 | Test with a CDATA section ]]> def 80 | Test with a char entity: A 81 | 82 | 1234567890ABCD 83 | 84 | "; 85 | var bytes = XmlToFlexBufferConverter.Convert(xml); 86 | 87 | var flx = FlxValue.FromBytes(bytes); 88 | 89 | Assert.AreEqual(2, flx.AsMap.Length); 90 | Assert.AreEqual("Items", flx["tagName"].AsString); 91 | Assert.AreEqual(5, flx["children"].AsVector.Length); 92 | 93 | Assert.AreEqual(2, flx["children"][0].AsMap.Length); 94 | Assert.AreEqual("Item", flx["children"][0]["tagName"].AsString); 95 | Assert.AreEqual(2, flx["children"][0]["children"].AsVector.Length); 96 | Assert.AreEqual("Test with an entity: ", flx["children"][0]["children"][0].AsString); 97 | Assert.AreEqual(2, flx["children"][0]["children"][1].AsMap.Length); 98 | Assert.AreEqual("number", flx["children"][0]["children"][1]["tagName"].AsString); 99 | Assert.AreEqual(1, flx["children"][0]["children"][1]["children"].AsVector.Length); 100 | Assert.AreEqual("123", flx["children"][0]["children"][1]["children"][0].AsString); 101 | 102 | Assert.AreEqual("Item", flx["children"][1]["tagName"].AsString); 103 | Assert.AreEqual(3, flx["children"][1]["children"].AsVector.Length); 104 | Assert.AreEqual("Test with a child element ", flx["children"][1]["children"][0].AsString); 105 | Assert.AreEqual(1, flx["children"][1]["children"][1].AsMap.Length); 106 | Assert.AreEqual("more", flx["children"][1]["children"][1]["tagName"].AsString); 107 | Assert.AreEqual(" stuff", flx["children"][1]["children"][2].AsString); 108 | 109 | Assert.AreEqual("Item", flx["children"][2]["tagName"].AsString); 110 | Assert.AreEqual(3, flx["children"][2]["children"].AsVector.Length); 111 | Assert.AreEqual("Test with a CDATA section ", flx["children"][2]["children"][0].AsString); 112 | Assert.AreEqual("<456>", flx["children"][2]["children"][1].AsString); 113 | Assert.AreEqual(" def", flx["children"][2]["children"][2].AsString); 114 | 115 | Assert.AreEqual("Item", flx["children"][3]["tagName"].AsString); 116 | Assert.AreEqual(1, flx["children"][3]["children"].AsVector.Length); 117 | Assert.AreEqual("Test with a char entity: A", flx["children"][3]["children"][0].AsString); 118 | 119 | Assert.AreEqual("Item", flx["children"][4]["tagName"].AsString); 120 | Assert.AreEqual(1, flx["children"][4]["children"].AsVector.Length); 121 | Assert.AreEqual("1234567890ABCD", flx["children"][4]["children"][0].AsString); 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /FlexBuffersCSharpTests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Maxim Zaks 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 | # FlexBuffers-CSharp 2 | FlexBuffers is JSON comparable binary format with random value access capabilities. 3 | The binary format and data layout was designed at Google as part of [FlatBuffers project](https://google.github.io/flatbuffers/flexbuffers.html). 4 | This projects brings FlexBuffers as a standalone solution to C# with the focus to convert large JSON files to randomly accessable FlexBuffers. 5 | 6 | The main focus of this project is to be used with Unity3D. 7 | However current solution has no Unity3D dependencies and should be usable in other C# environments. 8 | Please feel free to contribute tests and patches for other environments. 9 | 10 | ## Supported types 11 | As mentioned in the project description, FlexBuffers is a JSON comparable data format. It supports all the types JSON does, but also stores more detailed informations about the values. 12 | 13 | Here is the list of supported types, as of November 2019: 14 | - `Null` represents an absence of a value, specifically useful in collections 15 | - `Int` represents positive and negative integer numbers, FlexBuffers additionally stores the bit width of the number which can be 8, 16, 32 or 64 bits 16 | - `UInt` represents only positive numbers, also stores the bit width of 8, 16, 32 or 64 bits 17 | - `Float` represents a floating point number. The possible bit width is limited to 32 and 64 18 | - `Key` an internal zero terminated string representation for `Map` keys. Is not relevant for the end users 19 | - `String` a length prepanded, zero terminated, UTF-8 encoded text representation 20 | - `IndirectInt`, `IndirectUInt`, `IndirectFloat` are `Int`, `UInt` and `Float` stored by reference. Relevant for end users only if they want to achieve smallest binary size (will be discussed in a separate section) 21 | - `Map` is similar to JSON object. Keys are strings, values can be any other here listed supported type. Speaking in terms of C# it is a `Dictionary` 22 | - `Vector` is similar to JSON array. An array which can host any types. FlexBuffers stores the values together with value type information 23 | - `VecorInt`, `VectorUInt`, `VectorFloat`, `VectorKey`, `VectorString`, `VectorBool` are arrays of a given type. Given this type FlatBuffer does not store the type information for every individual entry and there for achieves smaller buffer size 24 | - `VectorInt2`, `VectorInt3`, `VectorInt4`, `VectorUInt2`, `VectorUInt3`, `VectorUInt4`, `VectorFloat2`, `VectorFloat3`, `VectorFloat4` is a special type of array which has a fix type and fix size of elements. A vector with non fixed size need to store size, however fix sized vectors don't have to do it as it is encoded in type directly 25 | - `Blob` stores a byte array 26 | - `Bool` stores bnoolean values `true` or `false` 27 | 28 | ## Creation of FlexBuffer 29 | There are multple ways how we can create a FlexBuffer 30 | 31 | ### Single value 32 | It is posible to store just one value in a FlexBuffer. It is not that probable to do so in day to day business, but was helpful for unit testing the format. 33 | - `FlexBuffer.Null()` returns a byte array, which represents a FlexBuffer with `null` as single value. 34 | - `FlexBuffer.SingleValue(value)` returns a byte array, which represents a FlexBuffer with as single value. The `value` parameter can be of type `long`, `ulong`, `string`, `bool` 35 | - `FlexBuffer.SingleValue(x, y)`, `FlexBuffer.SingleValue(x, y, z)`, `FlexBuffer.SingleValue(x, y, z, w)` creates a typed fixed size vector. Parameters `x`, `y`, `z`, `w` can be of type `long`, `ulong` or `double` 36 | - `FlexBuffer.From(dict)` creates a FlexBuffer based on a `Dictionary` instance. It is not the fastest but very convinient way of creating a FlexBuffer. It assumes that the values are of compatible types 37 | - `FlexBuffer.From(value)` where `value` is of type `IEnumerable` will try to create a FlexBuffer with `Vector` as root element 38 | - `FlexBuffer.SingleValue(new byte[]{1, 2, 3})`creates a FlexBuffer with `Blob` as single value. 39 | 40 | ### From JSON 41 | FlexBuffers-CSharp has a special type called `JsonToFlexBufferConverter` which allows user to convert a JSON string to FlexBuffer byte array. 42 | ``` 43 | var buffer = JsonToFlexBufferConverter.Convert("{\"a\":1, \"b\":2}"); 44 | ``` 45 | 46 | `JsonToFlexBufferConverter` Contains an actualt JSON parser (based on [LightJson](https://github.com/MarcosLopezC/LightJson)) and there for converts one byte array (JSON string) directly to another byte aray (FlexBuffer) efficient and with a minimal amount of temporary objects. 47 | 48 | ### FlexBuffer Builder 49 | The `FlexBufferBuilder` enable users to create FlexBuffers directly form values, without a need for temporary representations. 50 | The Root elelemnt can be defined as a `Map`: 51 | ``` 52 | var bytes = FlexBufferBuilder.Map(root => 53 | { 54 | root.Add("name", "Maxim"); 55 | root.Add("age", 38); 56 | root.Add("weight", 72.5); 57 | root.Map("address", address => 58 | { 59 | address.Add("city", "Bla"); 60 | address.Add("zip", "12345"); 61 | address.Add("countryCode", "XX"); 62 | }); 63 | root.Vector("flags", tags => 64 | { 65 | tags.Add(true); 66 | tags.Add(false); 67 | tags.Add(true); 68 | tags.Add(true); 69 | }); 70 | }); 71 | ``` 72 | 73 | Or as a `Vector`: 74 | ``` 75 | var bytes = FlexBufferBuilder.Vector(root => 76 | { 77 | root.AddNull(); 78 | root.Add(new byte[]{1,2,3}); 79 | root.Map(map => 80 | { 81 | map.AddNull("a"); 82 | map.Add("b", new byte[]{3,4,5}); 83 | }); 84 | }); 85 | ``` 86 | 87 | ## FlexBuffers to JSON 88 | As Flexbuffer has JSON compatible types it is very easy to conver a FlexBuffer to JSON. 89 | 90 | The FlexBuffer from previous paragraph can be converted to following JSON string: 91 | `[null,"AQID",{"a":null,"b":"AwQF"}]` 92 | `"AQID"` and `"AwQF"` are Base64 representations of `new byte[]{1,2,3}` and `new byte[]{3,4,5}` 93 | 94 | ## Byte array to FlexBuffer value 95 | With FlexBuffers we can extract values directly from the buffer, without any parsing or complex upfront conversions. 96 | With `var flx = FlxValue.FromBytes(bytes);` users can create an instance of a FlexBuffer value which allows conversion to types and access of sub elements. 97 | `FlxValue` struct has following getters: 98 | - `ValueType` returns the `Type` enum representing the value type (types are the cases listed Supported Types section) 99 | - `IsNull` return `true` or `false` if the value is `null` 100 | - `AsLong`, `AsULong`, `AsDouble`, `AsBool`, `AsString` and `AsBlob` tries to interpret the undelying value as expected type and returns the value 101 | - `AsVector` checks if the underlying value is one of the `Vector` types and returns an instace of `FlxVector` struct, which implements `IEnumerable` and has a `Length` getter 102 | - `AsMap` checks if the underlying value is a `Map` type and return an instance of `FlxMap` struct, which implements `IEnumerable>` and also has a `Length` getter 103 | - `ToJson` returns a JSON string (see FlexBuffers to JSON section) 104 | - User can wirte `flx[0]` in order to convert the `FlxValue` into `FlxVector` and access the first element of the vector 105 | - When user wirte `flx["a"]`, then `FlxValue` is converted to `FlxMap` and value for the key `"a"` is accessed 106 | 107 | --- 108 | 109 | # Outlook 110 | As next steps we are cosnidering: 111 | - object graph to FlexBuffer and FlexBuffer to object graph conversion 112 | - profiling and performance tuning 113 | 114 | Contribution is welcome. 115 | --------------------------------------------------------------------------------