1
1
using System ;
2
- using System . Collections . Generic ;
3
2
using System . IO ;
4
- using System . Linq ;
5
- using System . Runtime . Serialization ;
6
- using System . Text ;
3
+ using UnityEngine ;
7
4
using static MLAPI . NetworkingManagerComponents . Binary . Arithmetic ;
8
5
9
6
namespace MLAPI . NetworkingManagerComponents . Binary
@@ -13,6 +10,10 @@ public sealed class BitStream : Stream
13
10
const int initialCapacity = 16 ;
14
11
const float initialGrowthFactor = 2.0f ;
15
12
private byte [ ] target ;
13
+ private static readonly float [ ] holder_f = new float [ 1 ] ;
14
+ private static readonly double [ ] holder_d = new double [ 1 ] ;
15
+ private static readonly uint [ ] holder_i = new uint [ 1 ] ;
16
+ private static readonly ulong [ ] holder_l = new ulong [ 1 ] ;
16
17
17
18
/// <summary>
18
19
/// A stream that supports writing data smaller than a single byte. This stream also has a built-in compression algorithm that can (optionally) be used to write compressed data.
@@ -265,14 +266,251 @@ public override void Write(byte[] buffer, int offset, int count)
265
266
/// <param name="bit">Value of the bit. True represents 1, False represents 0</param>
266
267
public void WriteBit ( bool bit )
267
268
{
268
- if ( BitAligned && Position + 1 > = target . Length ) Grow ( 1 ) ;
269
+ if ( BitAligned && Position = = target . Length ) Grow ( 1 ) ;
269
270
int offset = ( int ) ( BitPosition & 7 ) ;
270
271
ulong pos = BitPosition >> 3 ;
271
272
++ BitPosition ;
272
273
target [ pos ] = ( byte ) ( bit ? ( target [ pos ] & ~ ( 1 << offset ) ) | ( 1 << offset ) : ( target [ pos ] & ~ ( 1 << offset ) ) ) ;
273
274
UpdateLength ( ) ;
274
275
}
275
276
277
+ /// <summary>
278
+ /// Write single-precision floating point value to the stream
279
+ /// </summary>
280
+ /// <param name="value">Value to write</param>
281
+ public void WriteSingle ( float value )
282
+ {
283
+ lock ( holder_f )
284
+ lock ( holder_i )
285
+ {
286
+ holder_f [ 0 ] = value ;
287
+ Buffer . BlockCopy ( holder_f , 0 , holder_i , 0 , 4 ) ;
288
+ WriteUInt32 ( holder_i [ 0 ] ) ;
289
+ }
290
+ }
291
+
292
+ /// <summary>
293
+ /// Write double-precision floating point value to the stream
294
+ /// </summary>
295
+ /// <param name="value">Value to write</param>
296
+ public void WriteDouble ( double value )
297
+ {
298
+ lock ( holder_d )
299
+ lock ( holder_l )
300
+ {
301
+ holder_d [ 0 ] = value ;
302
+ Buffer . BlockCopy ( holder_d , 0 , holder_l , 0 , 8 ) ;
303
+ WriteUInt64 ( holder_l [ 0 ] ) ;
304
+ }
305
+ }
306
+
307
+ /// <summary>
308
+ /// Write single-precision floating point value to the stream as a varint
309
+ /// </summary>
310
+ /// <param name="value">Value to write</param>
311
+ public void WriteSinglePacked ( float value )
312
+ {
313
+ lock ( holder_f )
314
+ lock ( holder_i )
315
+ {
316
+ holder_f [ 0 ] = value ;
317
+ Buffer . BlockCopy ( holder_f , 0 , holder_i , 0 , 4 ) ;
318
+ WriteUInt32Packed ( BinaryHelpers . SwapEndian ( holder_i [ 0 ] ) ) ;
319
+ }
320
+ }
321
+
322
+ /// <summary>
323
+ /// Write double-precision floating point value to the stream as a varint
324
+ /// </summary>
325
+ /// <param name="value">Value to write</param>
326
+ public void WriteDoublePacked ( double value )
327
+ {
328
+ lock ( holder_d )
329
+ lock ( holder_l )
330
+ {
331
+ holder_d [ 0 ] = value ;
332
+ Buffer . BlockCopy ( holder_d , 0 , holder_l , 0 , 8 ) ;
333
+ WriteUInt64Packed ( BinaryHelpers . SwapEndian ( holder_l [ 0 ] ) ) ;
334
+ }
335
+ }
336
+
337
+ /// <summary>
338
+ /// Convenience method that writes three non-varint floats from the vector to the stream
339
+ /// </summary>
340
+ /// <param name="vec">Vector to write</param>
341
+ public void WriteVector3 ( Vector3 vec )
342
+ {
343
+ WriteSingle ( vec . x ) ;
344
+ WriteSingle ( vec . y ) ;
345
+ WriteSingle ( vec . z ) ;
346
+ }
347
+
348
+ /// <summary>
349
+ /// Write a single-precision floating point value to the stream. The value is between (inclusive) the minValue and maxValue.
350
+ /// </summary>
351
+ /// <param name="value">Value to write</param>
352
+ /// <param name="minValue">Minimum value that this value could be</param>
353
+ /// <param name="maxValue">Maximum possible value that this could be</param>
354
+ /// <param name="bytes">How many bytes the compressed result should occupy. Must be between 1 and 4 (inclusive)</param>
355
+ public void WriteRangedSingle ( float value , float minValue , float maxValue , int bytes )
356
+ {
357
+ if ( bytes < 1 || bytes > 4 ) throw new ArgumentOutOfRangeException ( "Result must occupy between 1 and 4 bytes!" ) ;
358
+ if ( value < minValue || value > maxValue ) throw new ArgumentOutOfRangeException ( "Given value does not match the given constraints!" ) ;
359
+ uint result = ( uint ) ( ( ( value + minValue ) / ( maxValue + minValue ) ) * ( ( 0x100 * bytes ) - 1 ) ) ;
360
+ for ( int i = 0 ; i < bytes ; ++ i ) _WriteByte ( ( byte ) ( result >> ( i << 3 ) ) ) ;
361
+ }
362
+
363
+ /// <summary>
364
+ /// Write a double-precision floating point value to the stream. The value is between (inclusive) the minValue and maxValue.
365
+ /// </summary>
366
+ /// <param name="value">Value to write</param>
367
+ /// <param name="minValue">Minimum value that this value could be</param>
368
+ /// <param name="maxValue">Maximum possible value that this could be</param>
369
+ /// <param name="bytes">How many bytes the compressed result should occupy. Must be between 1 and 8 (inclusive)</param>
370
+ public void WriteRangedDouble ( double value , double minValue , double maxValue , int bytes )
371
+ {
372
+ if ( bytes < 1 || bytes > 8 ) throw new ArgumentOutOfRangeException ( "Result must occupy between 1 and 8 bytes!" ) ;
373
+ if ( value < minValue || value > maxValue ) throw new ArgumentOutOfRangeException ( "Given value does not match the given constraints!" ) ;
374
+ ulong result = ( ulong ) ( ( ( value + minValue ) / ( maxValue + minValue ) ) * ( ( 0x100 * bytes ) - 1 ) ) ;
375
+ for ( int i = 0 ; i < bytes ; ++ i ) _WriteByte ( ( byte ) ( result >> ( i << 3 ) ) ) ;
376
+ }
377
+
378
+ /// <summary>
379
+ /// Write a rotation to the stream.
380
+ /// </summary>
381
+ /// <param name="rotation">Rotation to write</param>
382
+ /// <param name="bytesPerAngle">How many bytes each written angle should occupy</param>
383
+ public void WriteRotation ( Quaternion rotation , int bytesPerAngle )
384
+ {
385
+ if ( bytesPerAngle < 1 || bytesPerAngle > 4 ) throw new ArgumentOutOfRangeException ( "Bytes per angle must be at least 1 byte and at most 4 bytes!" ) ;
386
+ if ( bytesPerAngle == 4 ) WriteVector3 ( rotation . eulerAngles ) ;
387
+ else
388
+ {
389
+ Vector3 rot = rotation . eulerAngles ;
390
+ WriteRangedSingle ( rot . x , 0f , 360f , bytesPerAngle ) ;
391
+ WriteRangedSingle ( rot . y , 0f , 360f , bytesPerAngle ) ;
392
+ WriteRangedSingle ( rot . z , 0f , 360f , bytesPerAngle ) ;
393
+ }
394
+ }
395
+
396
+ /// <summary>
397
+ /// Read a single-precision floating point value from the stream.
398
+ /// </summary>
399
+ /// <returns>The read value</returns>
400
+ public float ReadSingle ( )
401
+ {
402
+ uint read = ReadUInt32 ( ) ;
403
+ lock ( holder_f )
404
+ lock ( holder_i )
405
+ {
406
+ holder_i [ 0 ] = read ;
407
+ Buffer . BlockCopy ( holder_i , 0 , holder_f , 0 , 4 ) ;
408
+ return holder_f [ 0 ] ;
409
+ }
410
+ }
411
+
412
+
413
+ /// <summary>
414
+ /// Read a double-precision floating point value from the stream.
415
+ /// </summary>
416
+ /// <returns>The read value</returns>
417
+ public double ReadDouble ( )
418
+ {
419
+ ulong read = ReadUInt64 ( ) ;
420
+ lock ( holder_d )
421
+ lock ( holder_l )
422
+ {
423
+ holder_l [ 0 ] = read ;
424
+ Buffer . BlockCopy ( holder_l , 0 , holder_d , 0 , 8 ) ;
425
+ return holder_d [ 0 ] ;
426
+ }
427
+ }
428
+
429
+ /// <summary>
430
+ /// Read a single-precision floating point value from the stream from a varint
431
+ /// </summary>
432
+ /// <returns>The read value</returns>
433
+ public float ReadSinglePacked ( )
434
+ {
435
+ uint read = ReadUInt32Packed ( ) ;
436
+ lock ( holder_f )
437
+ lock ( holder_i )
438
+ {
439
+ holder_i [ 0 ] = BinaryHelpers . SwapEndian ( read ) ;
440
+ Buffer . BlockCopy ( holder_i , 0 , holder_f , 0 , 4 ) ;
441
+ return holder_f [ 0 ] ;
442
+ }
443
+ }
444
+
445
+ /// <summary>
446
+ /// Read a double-precision floating point value from the stream as a varint
447
+ /// </summary>
448
+ /// <returns>The read value</returns>
449
+ public double ReadDoublePacked ( )
450
+ {
451
+ ulong read = ReadUInt64Packed ( ) ;
452
+ lock ( holder_d )
453
+ lock ( holder_l )
454
+ {
455
+ holder_l [ 0 ] = BinaryHelpers . SwapEndian ( read ) ;
456
+ Buffer . BlockCopy ( holder_l , 0 , holder_d , 0 , 8 ) ;
457
+ return holder_d [ 0 ] ;
458
+ }
459
+ }
460
+
461
+ /// <summary>
462
+ /// Read a Vector3 from the stream.
463
+ /// </summary>
464
+ /// <returns>The Vector3 read from the stream.</returns>
465
+ public Vector3 ReadVector3 ( ) => new Vector3 ( ReadSingle ( ) , ReadSingle ( ) , ReadSingle ( ) ) ;
466
+
467
+ /// <summary>
468
+ /// Read a single-precision floating point value from the stream. The value is between (inclusive) the minValue and maxValue.
469
+ /// </summary>
470
+ /// <param name="minValue">Minimum value that this value could be</param>
471
+ /// <param name="maxValue">Maximum possible value that this could be</param>
472
+ /// <param name="bytes">How many bytes the compressed value occupies. Must be between 1 and 4 (inclusive)</param>
473
+ /// <returns>The read value</returns>
474
+ public float ReadRangedSingle ( float minValue , float maxValue , int bytes )
475
+ {
476
+ if ( bytes < 1 || bytes > 4 ) throw new ArgumentOutOfRangeException ( "Result must occupy between 1 and 4 bytes!" ) ;
477
+ uint read = 0 ;
478
+ for ( int i = 0 ; i < bytes ; ++ i ) read |= ( uint ) _ReadByte ( ) << ( i << 3 ) ;
479
+ return ( ( ( float ) read / ( ( 0x100 * bytes ) - 1 ) ) * ( minValue + maxValue ) ) - minValue ;
480
+ }
481
+
482
+ /// <summary>
483
+ /// read a double-precision floating point value from the stream. The value is between (inclusive) the minValue and maxValue.
484
+ /// </summary>
485
+ /// <param name="value">Value to write</param>
486
+ /// <param name="minValue">Minimum value that this value could be</param>
487
+ /// <param name="maxValue">Maximum possible value that this could be</param>
488
+ /// <param name="bytes">How many bytes the compressed value occupies. Must be between 1 and 8 (inclusive)</param>
489
+ /// <returns>The read value</returns>
490
+ public double ReadRangedDouble ( double minValue , double maxValue , int bytes )
491
+ {
492
+ if ( bytes < 1 || bytes > 8 ) throw new ArgumentOutOfRangeException ( "Result must occupy between 1 and 8 bytes!" ) ;
493
+ ulong read = 0 ;
494
+ for ( int i = 0 ; i < bytes ; ++ i ) read |= ( ulong ) _ReadByte ( ) << ( i << 3 ) ;
495
+ return ( ( ( double ) read / ( ( 0x100 * bytes ) - 1 ) ) * ( minValue + maxValue ) ) - minValue ;
496
+ }
497
+
498
+ /// <summary>
499
+ /// Read a rotation from the stream.
500
+ /// </summary>
501
+ /// <param name="bytesPerAngle">How many bytes each angle occupies</param>
502
+ /// <returns>The rotation read from the stream</returns>
503
+ public Quaternion ReadRotation ( int bytesPerAngle )
504
+ {
505
+ if ( bytesPerAngle < 1 || bytesPerAngle > 4 ) throw new ArgumentOutOfRangeException ( "Bytes per angle must be at least 1 byte and at most 4 bytes!" ) ;
506
+ if ( bytesPerAngle == 4 ) return Quaternion . Euler ( ReadVector3 ( ) ) ;
507
+ else return Quaternion . Euler (
508
+ ReadRangedSingle ( 0f , 360f , bytesPerAngle ) , // X
509
+ ReadRangedSingle ( 0f , 360f , bytesPerAngle ) , // Y
510
+ ReadRangedSingle ( 0f , 360f , bytesPerAngle ) // Z
511
+ ) ;
512
+ }
513
+
276
514
/// <summary>
277
515
/// Write the lower half (lower nibble) of a byte.
278
516
/// </summary>
0 commit comments