Skip to content

Commit 7ac8472

Browse files
Added floating point serialization
Added rotation serialization Added vector serialization
1 parent de35af0 commit 7ac8472

File tree

2 files changed

+243
-6
lines changed

2 files changed

+243
-6
lines changed

MLAPI.Tests/NetworkingManagerComponents/Binary/BitStreamTest.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ namespace MLAPI.Tests.NetworkingManagerComponents.Binary
33
{
44
using MLAPI.NetworkingManagerComponents.Binary;
55
using NUnit.Framework;
6-
using static MLAPI.NetworkingManagerComponents.Binary.BitStream;
76

87
[TestFixture]
98
public class BitStreamTest

MLAPI/NetworkingManagerComponents/Binary/BitStream.cs

Lines changed: 243 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
using System;
2-
using System.Collections.Generic;
32
using System.IO;
4-
using System.Linq;
5-
using System.Runtime.Serialization;
6-
using System.Text;
3+
using UnityEngine;
74
using static MLAPI.NetworkingManagerComponents.Binary.Arithmetic;
85

96
namespace MLAPI.NetworkingManagerComponents.Binary
@@ -13,6 +10,10 @@ public sealed class BitStream : Stream
1310
const int initialCapacity = 16;
1411
const float initialGrowthFactor = 2.0f;
1512
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];
1617

1718
/// <summary>
1819
/// 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)
265266
/// <param name="bit">Value of the bit. True represents 1, False represents 0</param>
266267
public void WriteBit(bool bit)
267268
{
268-
if (BitAligned && Position + 1 >= target.Length) Grow(1);
269+
if (BitAligned && Position == target.Length) Grow(1);
269270
int offset = (int)(BitPosition & 7);
270271
ulong pos = BitPosition >> 3;
271272
++BitPosition;
272273
target[pos] = (byte)(bit ? (target[pos] & ~(1 << offset)) | (1 << offset) : (target[pos] & ~(1 << offset)));
273274
UpdateLength();
274275
}
275276

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+
276514
/// <summary>
277515
/// Write the lower half (lower nibble) of a byte.
278516
/// </summary>

0 commit comments

Comments
 (0)