Skip to content

Commit af1be5b

Browse files
committed
FEAT: enhanced BINCODE dialect with new features:
* added /with refinement to provide additional value for reading single value like: `binary/read/with b 'BYTES 42` which returns just the binary and not a block with binary. * it's now possible to use zero bits without range error: `binary/read b [UB 0]` returns 0. * new read command `FIXED8` (16-bit 8.8 fixed-point number) * new read command `FIXED16` (32-bit 16.16 fixed-point number) * new read commands `TUPLE3` and `TUPLE4` (returns tuple type value from 3 or 4 bytes) * new read command `SkipBits` (allows to skip given number of bits without reading anything) * new read command `ALIGN` (aligns bit stream to byte boundary) * new read commands `FLOAT16`, `FLOAT` and `DOUBLE` (16bit, 32bit and 64bit decimal value) * new read command `SI16LE` (16bit signed integer using little-endian byte order) * new read command `FB` (Signed, fixed-point bit value) * fixed read command `SB`
1 parent 7654538 commit af1be5b

File tree

2 files changed

+215
-33
lines changed

2 files changed

+215
-33
lines changed

src/core/u-bincode.c

+152-29
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,41 @@
7373
#include <stdio.h>
7474
#include <time.h>
7575

76+
// FLOAT16 credits: Steven Pigeon
77+
// https://hbfs.wordpress.com/2013/02/12/float16/
78+
typedef union {
79+
// float16 v;
80+
struct {
81+
// type determines alignment!
82+
u16 m : 10;
83+
u16 e : 5;
84+
u16 s : 1;
85+
} bits;
86+
struct {
87+
u8 low;
88+
u8 high;
89+
} bytes;
90+
} float16_s;
91+
92+
typedef union {
93+
float v;
94+
struct {
95+
u32 m : 23;
96+
u32 e : 8;
97+
u32 s : 1;
98+
} bits;
99+
} float32_s;
100+
101+
float float16to32(float16_s f16) {
102+
// back to 32
103+
float32_s f32;
104+
f32.bits.s = f16.bits.s;
105+
f32.bits.e = (f16.bits.e - 15) + 127; // safe in this direction
106+
f32.bits.m = ((u32)f16.bits.m) << 13;
107+
return f32.v;
108+
}
109+
110+
76111
#define ASSERT_SI_RANGE(v, n) if (VAL_INT64(v) < (- (i64)n) || VAL_INT64(v) > (i64)n) Trap1(RE_OUT_OF_RANGE, v);
77112
#define ASSERT_UI_RANGE(v, n) if (VAL_INT32(v) > n) Trap1(RE_OUT_OF_RANGE, v);
78113
#define ASSERT_UIBYTES_RANGE(v, n) if (VAL_LEN(v) > n) Trap1(RE_OUT_OF_RANGE, v);
@@ -141,6 +176,8 @@ static REBCNT EncodedU32_Size(u32 value) {
141176
// code [word! block!] "Input encoding"
142177
// /into "Put READ results in out block, instead of creating a new block"
143178
// out [block!] "Target block for results, when /into is used"
179+
// /with "Additional input argument"
180+
// num [integer!] "Bits/bytes number used with WORD! code type to resolve just single value"
144181
// ]
145182
***********************************************************************/
146183
{
@@ -153,6 +190,8 @@ static REBCNT EncodedU32_Size(u32 value) {
153190
REBVAL *val_read = D_ARG(7);
154191
REBOOL ref_into = D_REF(8);
155192
REBVAL *val_into = D_ARG(9);
193+
REBOOL ref_with = D_REF(10);
194+
REBVAL *val_num = D_ARG(11);
156195

157196
REBVAL *ret = D_RET;
158197
//REBVAL *buf;
@@ -766,6 +805,7 @@ static REBCNT EncodedU32_Size(u32 value) {
766805
// if encoding is just a word, simulate block with single value on stack
767806
DS_PUSH(val_read);
768807
value = DS_TOP;
808+
if (IS_INTEGER(val_num)) DS_PUSH(val_num);
769809
DS_PUSH_END; // marks end of the block
770810
if(ref_into) {
771811
blk = VAL_SERIES(val_into);
@@ -778,12 +818,14 @@ static REBCNT EncodedU32_Size(u32 value) {
778818
DS_PUSH_NONE;
779819
temp = DS_TOP;
780820
REBINT ssp = DSP; // starting stack pointer
821+
REBINT cmd;
781822

782823
for (; NOT_END(value); value++) {
783824
n = 0;
784825
switch (VAL_TYPE(value)) {
785826
case REB_WORD:
786-
switch (VAL_WORD_CANON(value)) {
827+
cmd = VAL_WORD_CANON(value);
828+
switch (cmd) {
787829
case SYM_UI8:
788830
n = 1;
789831
ASSERT_READ_SIZE(value, cp, ep, n);
@@ -810,6 +852,12 @@ static REBCNT EncodedU32_Size(u32 value) {
810852
VAL_SET(temp, REB_INTEGER);
811853
SET_INT32(temp, (i16)((i16)cp[1] + ((i16)cp[0] << 8)));
812854
break;
855+
case SYM_SI16LE:
856+
n = 2;
857+
ASSERT_READ_SIZE(value, cp, ep, n);
858+
VAL_SET(temp, REB_INTEGER);
859+
SET_INT32(temp, (i16)((i16)cp[0] + ((i16)cp[1] << 8)));
860+
break;
813861
case SYM_UI24:
814862
case SYM_UI24BE:
815863
n = 3;
@@ -967,48 +1015,55 @@ static REBCNT EncodedU32_Size(u32 value) {
9671015
if (IS_GET_WORD(next)) next = Get_Var(next);
9681016
if (!IS_INTEGER(next)) Trap1(RE_INVALID_SPEC, value);
9691017
i = 0;
970-
if (inBit == 0) inBit = 0x80;
9711018
// could be optimized?
9721019
nbits = VAL_INT32(next);
9731020
//printf("bits: %i %i\n", nbits, 1 << nbits);
974-
nbits = 1 << nbits;
975-
ASSERT_READ_SIZE(value, cp, ep, 1);
976-
while(nbits > 1) {
977-
nbits = nbits >> 1;
978-
if(IS_BIT_SET(cp[0], inBit)) i = i | nbits;
979-
//printf("?? %i %i\n", inBit, i);
980-
NEXT_IN_BIT(inBit);
981-
//printf("inBit: %i\n", inBit);
1021+
if (nbits > 0) {
1022+
if (inBit == 0) inBit = 0x80;
1023+
nbits = 1 << nbits;
1024+
ASSERT_READ_SIZE(value, cp, ep, 1);
1025+
while(nbits > 1) {
1026+
nbits = nbits >> 1;
1027+
if(IS_BIT_SET(cp[0], inBit)) i = i | nbits;
1028+
//printf("?? %i %i\n", inBit, i);
1029+
NEXT_IN_BIT(inBit);
1030+
//printf("inBit: %i\n", inBit);
1031+
}
1032+
STORE_IN_BIT(val_ctx, inBit);
9821033
}
9831034
VAL_SET(temp, REB_INTEGER);
9841035
SET_INT32(temp, i);
985-
STORE_IN_BIT(val_ctx, inBit);
9861036
break;
9871037
case SYM_SB:
1038+
case SYM_FB:
9881039
next = ++value;
9891040
if (IS_GET_WORD(next)) next = Get_Var(next);
9901041
if (!IS_INTEGER(next)) Trap1(RE_INVALID_SPEC, value);
991-
i = 0;
992-
if (inBit == 0) inBit = 0x80;
1042+
u = 0;
9931043
// could be optimized?
9941044
nbits = VAL_INT32(next);
995-
nbits = 1 << nbits;
9961045
if (nbits > 0) {
997-
//printf("nbits: %i\n", nbits);
998-
ASSERT_READ_SIZE(value, cp, ep, 1);
999-
BOOL negative = IS_BIT_SET(cp[0], inBit);
1000-
nbits = nbits >> 1;
1001-
NEXT_IN_BIT(inBit);
1002-
while (nbits > 1) {
1003-
nbits = nbits >> 1;
1004-
if (IS_BIT_SET(cp[0], inBit)) i = i | nbits;
1005-
//printf("?? %i %i\n", inBit, i);
1006-
NEXT_IN_BIT(inBit);
1007-
}
1008-
if(negative) i = -i;
1046+
if (inBit == 0) inBit = 0x80;
1047+
// http://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend
1048+
u64 m = 1U << (nbits - 1); // sign bit mask
1049+
nbits = 1 << nbits;
1050+
//if (nbits > 0) {
1051+
//printf("SB nbits: %i\n", nbits);
1052+
while (nbits > 1) {
1053+
nbits >>= 1;
1054+
if (IS_BIT_SET(cp[0], inBit)) u = u | nbits;
1055+
//printf("?? %i %i %u\n", nbits, inBit, u);
1056+
NEXT_IN_BIT(inBit);
1057+
}
1058+
u = (u ^ m) - m;
1059+
//}
1060+
STORE_IN_BIT(val_ctx, inBit);
1061+
}
1062+
if (cmd == SYM_SB) {
1063+
SET_INTEGER(temp, u);
1064+
} else {
1065+
SET_DECIMAL(temp, (double)u / 65536.0);
10091066
}
1010-
VAL_SET(temp, REB_INTEGER);
1011-
SET_INT32(temp, i);
10121067
break;
10131068
case SYM_BIT:
10141069
case SYM_NOT_BIT:
@@ -1027,7 +1082,8 @@ static REBCNT EncodedU32_Size(u32 value) {
10271082
break;
10281083
case SYM_ALIGN:
10291084
// aligns bit buffer to byte boundary
1030-
if (inBit > 0) {
1085+
//if (inBit == 128) inBit = 0;
1086+
if (inBit > 0 && inBit < 128) {
10311087
inBit = 0;
10321088
cp++;
10331089
VAL_INDEX(buffer_read)++;
@@ -1145,10 +1201,75 @@ static REBCNT EncodedU32_Size(u32 value) {
11451201
VAL_INDEX(buffer_read) = i; //TODO: range test
11461202
cp = BIN_DATA(bin) + VAL_INDEX(buffer_read);
11471203
continue;
1204+
case SYM_SKIPBITS:
1205+
next = ++value;
1206+
if (IS_GET_WORD(next)) next = Get_Var(next);
1207+
if (!IS_INTEGER(next)) Trap1(RE_INVALID_SPEC, value);
1208+
i = VAL_INT32(next);
1209+
if (i >= 8) {
1210+
i /= 8;
1211+
//printf("byte skip: %d\n", i);
1212+
ASSERT_READ_SIZE(value, cp, ep, i);
1213+
cp += i;
1214+
VAL_INDEX(buffer_read) += i;
1215+
i = VAL_INT32(next) - (i * 8);
1216+
}
1217+
if (inBit == 0) inBit = 0x80;
1218+
while (i > 0) {
1219+
i--;
1220+
//printf("inbit %d: %d %d\n",i, inBit, VAL_INDEX(buffer_read));
1221+
NEXT_IN_BIT(inBit);
1222+
}
1223+
continue;
11481224
case SYM_LENGTHQ:
11491225
VAL_SET(temp, REB_INTEGER);
11501226
SET_INT32(temp, VAL_TAIL(buffer_read) - VAL_INDEX(buffer_read));
11511227
break;
1228+
case SYM_TUPLE3:
1229+
n = 3;
1230+
goto readNTuple;
1231+
case SYM_TUPLE4:
1232+
n = 4;
1233+
readNTuple:
1234+
ASSERT_READ_SIZE(value, cp, ep, n);
1235+
Set_Tuple(temp, BIN_DATA(bin) + VAL_INDEX(buffer_read), n);
1236+
break;
1237+
case SYM_FLOAT16:
1238+
n = 2;
1239+
ASSERT_READ_SIZE(value, cp, ep, n);
1240+
float16_s f16;
1241+
f16.bytes.low = cp[0];
1242+
f16.bytes.high = cp[1];
1243+
SET_DECIMAL(temp, float16to32(f16) );
1244+
break;
1245+
case SYM_FLOAT:
1246+
n = 4;
1247+
ASSERT_READ_SIZE(value, cp, ep, n);
1248+
SET_DECIMAL(temp, ((float*)cp)[0]);
1249+
break;
1250+
case SYM_DOUBLE:
1251+
n = 8;
1252+
ASSERT_READ_SIZE(value, cp, ep, n);
1253+
SET_DECIMAL(temp, ((double*)cp)[0]);
1254+
break;
1255+
case SYM_FIXED8:
1256+
n = 2;
1257+
ASSERT_READ_SIZE(value, cp, ep, n);
1258+
i = ((i32)cp[0] << 0) |
1259+
((i32)cp[1] << 8) ;
1260+
SET_DECIMAL(temp, (float)i / 256.0f);
1261+
break;
1262+
case SYM_FIXED16:
1263+
n = 4;
1264+
ASSERT_READ_SIZE(value, cp, ep, n);
1265+
i = ((i32)cp[0] << 0) |
1266+
((i32)cp[1] << 8) |
1267+
((i32)cp[2] << 16) |
1268+
((i32)cp[3] << 24) ;
1269+
VAL_SET(temp, REB_DECIMAL);
1270+
VAL_DECIMAL(temp) = ((float)i / 65536.0f);
1271+
break;
1272+
11521273
default:
11531274
Trap1(RE_INVALID_SPEC, value);
11541275
}
@@ -1184,6 +1305,8 @@ static REBCNT EncodedU32_Size(u32 value) {
11841305
DS_DROP; // temp
11851306
DS_DROP; // END of the virtual block
11861307
DS_DROP; // value
1308+
if (IS_INTEGER(val_num)) DS_DROP;
1309+
//@@ could above be done better?
11871310
}
11881311

11891312
if(ref_into) *ret = *val_into;

src/tests/units/bincode-test.r3

+63-4
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ is-protected-error?: func[code][
133133
; results:
134134
--assert [255 65535 16777215 4294967295] = binary/read b [UI8 UI16 UI24 UI32]
135135

136+
--test-- "BinCode - SI16LE, SI16BE"
137+
--assert 1 = binary/read #{0100} 'SI16LE
138+
--assert 1 = binary/read #{0001} 'SI16BE
139+
136140
b: binary 32
137141
--test-- "BinCode - BYTES"
138142
--assert object? binary/write b [#{cafe}]
@@ -226,9 +230,31 @@ is-protected-error?: func[code][
226230
--assert str = "test"
227231
--assert i = 42
228232

229-
--test-- "BinCode - bits (SB, UB, ALIGN)"
233+
--test-- "BinCode - bits (SB, UB, FB, ALIGN)"
230234
b: binary 2#{01011011 10110011 11111111}
231-
--assert [2 -2 3 -3 255] = binary/read b [SB 3 SB 3 UB 2 SB 4 ALIGN UI8]
235+
--assert [2 -2 3 -5 255] = binary/read b [SB 3 SB 3 UB 2 SB 4 ALIGN UI8]
236+
--assert [-2 6] = binary/read 2#{1110 0110} [SB 4 SB 4]
237+
--assert 14 = binary/read/with 2#{1110 0000} 'UB 4
238+
--assert [2.5] = binary/read #{500000} [FB 19]
239+
240+
--test-- "BinCode - bits (variant using sigle value access)"
241+
bin: binary #{438E9438}
242+
--assert 1080 = binary/read/with bin 'SB 12
243+
--assert binary/read bin 'BIT
244+
--assert binary/read bin 'BIT
245+
--assert 10 = binary/read/with bin 'UB 4
246+
--assert not binary/read bin 'BIT
247+
--assert binary/read bin 'BIT
248+
--assert 1080 = binary/read/with bin 'SB 12
249+
--assert 2.5 = binary/read/with #{500000} 'FB 19
250+
251+
bin: binary #{438E9438}
252+
binary/read bin [a: SB 12 BIT BIT b: UB 4 BIT BIT c: SB 12]
253+
--assert all [a = 1080 b = 10 c = 1080]
254+
255+
--test-- "BinCode - bits with zero skip"
256+
; shuld not throw range error when bits number is 0
257+
--assert [0 0 0] = binary/read #{00} [UI8 SB 0 UB 0]
232258

233259
--test-- "BinCode - EncodedU32"
234260
b: binary/init none 16
@@ -263,10 +289,43 @@ is-protected-error?: func[code][
263289
not any [f32/1 f32/2 f32/3 f32/4 f32/5 f32/6 f32/7 f32/15]
264290
]
265291

266-
===end-group===
292+
--test-- "BinCode - FIXED8 and FIXED16 (read)"
293+
binary/read #{800700800700} [
294+
f8: FIXED8
295+
f16: FIXED16
296+
]
297+
--assert 7.5 = f8
298+
--assert 7.5 = f16
299+
300+
--test-- "BinCode - TUPLE3 and TUPLE4 (read)"
301+
binary/read #{01020304050607} [
302+
rgb: TUPLE3
303+
rgba: TUPLE4
304+
]
305+
--assert 1.2.3 = rgb
306+
--assert 4.5.6.7 = rgba
267307

308+
--test-- "BinCode - SKIPBITS"
309+
--assert [2 3] = binary/read 2#{00000000 11000011} [
310+
SKIPBITS 9 UB 2
311+
SKIPBITS 3 UB 2
312+
]
313+
314+
--test-- "BinCode - ALIGN"
315+
--assert [0 1 2] = binary/read #{008002} [
316+
UB 8 ALIGN ; align on byte boundary is noop
317+
UB 1 ALIGN ; this align should move input pointer
318+
UI8 ; and this read should return value 2
319+
]
320+
321+
--test-- "BinCode - FLOAT16, FLOAT, DOUBLE (read)"
322+
--assert 1.0 = binary/read #{003C} 'FLOAT16
323+
--assert 1.0 = binary/read #{0000803F} 'FLOAT
324+
--assert 1.0 = binary/read #{000000000000F03F} 'DOUBLE
325+
326+
327+
===end-group===
268328

269329

270-
probe
271330

272331
~~~end-file~~~

0 commit comments

Comments
 (0)