Skip to content

Commit

Permalink
lib::std::encoding: add varint::{read,write}
Browse files Browse the repository at this point in the history
Signed-off-by: Pierre Curto <[email protected]>
  • Loading branch information
pierrec authored and lerno committed Sep 22, 2023
1 parent e706a8a commit d6edd80
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 0 deletions.
58 changes: 58 additions & 0 deletions lib/std/io/stream.c3
Original file line number Diff line number Diff line change
Expand Up @@ -383,3 +383,61 @@ macro usz! copy_through_buffer(Stream *self, Stream* dst, char[] buffer) @local
if (written != len) return IoError.INCOMPLETE_WRITE?;
}
}

const char[*] MAX_VARS @private = { [2] = 3, [4] = 5, [8] = 10 };

/**
* @require $typeof(x_ptr).kindof == POINTER && $typeof(x_ptr).inner.kindof.is_int()
**/
macro usz! Stream.read_varint(&self, x_ptr)
{
var $Type = $typefrom($typeof(x_ptr).inner);
const MAX = MAX_VARS[$Type.sizeof];
$Type x;
uint shift;
usz n;
for (usz i = 0; i < MAX; i++)
{
char! c = self.read_byte();
if (catch err = c)
{
case IoError.EOF:
return IoError.UNEXPECTED_EOF?;
default:
return err?;
}
n++;
if (c & 0x80 == 0)
{
if (i + 1 == MAX && c > 1) break;
x |= c << shift;
$if $Type.kindof == SIGNED_INT:
x = x & 1 == 0 ? x >> 1 : ~(x >> 1);
$endif
*x_ptr = x;
return n;
}
x |= (c & 0x7F) << shift;
shift += 7;
}
return MathError.OVERFLOW?;
}

/**
* @require $typeof(x).kindof.is_int()
**/
macro usz! Stream.write_varint(&self, x)
{
var $Type = $typeof(x);
const MAX = MAX_VARS[$Type.sizeof];
char[MAX] buffer;
usz i;
while (x >= 0x80)
{
buffer[i] = (char)(x | 0x80);
x >>= 7;
i++;
}
buffer[i] = (char)x;
return self.write_all(buffer[:i + 1]);
}
54 changes: 54 additions & 0 deletions test/unit/stdlib/io/varint.c3
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
module std::io::varint @test;
import std::io;

fn void! write_read()
{
ByteBuffer buf;
buf.tinit(16)!;
usz n;
uint x;
uint y;

n = buf.write_varint(123)!;
assert(n == 1, "got %d; want 1", n);
buf.read_varint(&y)!;
assert(y == 123, "got %d; want 123", y);

n = buf.write_varint(123456789)!;
assert(n == 4, "got %d; want 4", n);
buf.read_varint(&y)!;
assert(y == 123456789, "got %d; want 123456789", y);
}

struct VarIntTest
{
uint in;
char[] bytes;
}

fn void! samples()
{
VarIntTest[] tcases = {
{ 0, { 0x00 } },
{ 100, { 0x64 } },
{ 127, { 0x7F } },
{ 128, { 0x80, 0x01 } },
{ 16271, { 0x8F, 0x7F } },
{ 16383, { 0xFF, 0x7F } },
{ 16384, { 0x80, 0x80, 0x01 } },
{ 1048576, { 0x80, 0x80, 0x40 } },
{ 2097151, { 0xFF, 0xFF, 0x7F } },
{ 2097152, { 0x80, 0x80, 0x80, 0x01 } },
{ 2147483648, { 0x80, 0x80, 0x80, 0x80, 0x08 } },
{ 4294967295, { 0xFF, 0xFF, 0xFF, 0xFF, 0x0F } },
};
foreach (tc : tcases)
{
ByteWriter bw;
bw.tinit();
usz n = bw.write_varint(tc.in)!;
assert(n == tc.bytes.len, "got %d; want %d", n, tc.bytes.len);
char[] bytes = bw.bytes[:bw.index];
assert(bytes == tc.bytes, "got %d; want %d", bytes, tc.bytes);
}
}

0 comments on commit d6edd80

Please sign in to comment.