Skip to content

Commit 2fdc3d2

Browse files
committed
implement variable-length-quantity exercise
1 parent 61e26b9 commit 2fdc3d2

13 files changed

Lines changed: 4463 additions & 0 deletions

File tree

config.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,14 @@
635635
"structs"
636636
]
637637
},
638+
{
639+
"slug": "variable-length-quantity",
640+
"name": "Variable Length Quantity",
641+
"uuid": "318e1e7c-fc3a-4ec6-a47d-f8526d032b4f",
642+
"practices": [],
643+
"prerequisites": [],
644+
"difficulty": 4
645+
},
638646
{
639647
"slug": "spiral-matrix",
640648
"name": "Spiral Matrix",
@@ -1263,3 +1271,4 @@
12631271
"used_for/scientific_calculations"
12641272
]
12651273
}
1274+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Instructions
2+
3+
Implement variable length quantity encoding and decoding.
4+
5+
The goal of this exercise is to implement [VLQ][vlq] encoding/decoding.
6+
7+
In short, the goal of this encoding is to encode integer values in a way that would save bytes.
8+
Only the first 7 bits of each byte are significant (right-justified; sort of like an ASCII byte).
9+
So, if you have a 32-bit value, you have to unpack it into a series of 7-bit bytes.
10+
Of course, you will have a variable number of bytes depending upon your integer.
11+
To indicate which is the last byte of the series, you leave bit #7 clear.
12+
In all of the preceding bytes, you set bit #7.
13+
14+
So, if an integer is between `0-127`, it can be represented as one byte.
15+
Although VLQ can deal with numbers of arbitrary sizes, for this exercise we will restrict ourselves to only numbers that fit in a 32-bit unsigned integer.
16+
Here are examples of integers as 32-bit values, and the variable length quantities that they translate to:
17+
18+
```text
19+
NUMBER VARIABLE QUANTITY
20+
00000000 00
21+
00000040 40
22+
0000007F 7F
23+
00000080 81 00
24+
00002000 C0 00
25+
00003FFF FF 7F
26+
00004000 81 80 00
27+
00100000 C0 80 00
28+
001FFFFF FF FF 7F
29+
00200000 81 80 80 00
30+
08000000 C0 80 80 00
31+
0FFFFFFF FF FF FF 7F
32+
```
33+
34+
[vlq]: https://en.wikipedia.org/wiki/Variable-length_quantity
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"authors": [
3+
"Wiguwbe"
4+
],
5+
"files": {
6+
"solution": [
7+
"variable_length_quantity.c",
8+
"variable_length_quantity.h"
9+
],
10+
"test": [
11+
"test_variable_length_quantity.c"
12+
],
13+
"example": [
14+
".meta/example.c",
15+
".meta/example.h"
16+
]
17+
},
18+
"blurb": "Implement variable length quantity encoding and decoding.",
19+
"source": "A poor Splice developer having to implement MIDI encoding/decoding.",
20+
"source_url": "https://splice.com"
21+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#include "variable_length_quantity.h"
2+
3+
#include <string.h>
4+
5+
#define I_BUF_SIZE 5
6+
7+
#define SQLite_VARINT 0
8+
9+
int encode(uint64_t *integers, int integers_len, uint8_t *output)
10+
{
11+
int output_index = 0;
12+
uint8_t buf[I_BUF_SIZE];
13+
for (int i = 0; i < integers_len; i++) {
14+
uint64_t integer = integers[i];
15+
uint8_t mask = 0;
16+
int buf_idx = I_BUF_SIZE;
17+
do {
18+
uint8_t part = (uint8_t)(integer & 0x7f);
19+
integer >>= 7;
20+
buf[--buf_idx] = part | mask;
21+
mask |= 0x80;
22+
} while (integer);
23+
int buf_len = I_BUF_SIZE - buf_idx;
24+
memcpy(output + output_index, buf + buf_idx, buf_len);
25+
output_index += buf_len;
26+
}
27+
return output_index;
28+
}
29+
30+
int decode(uint8_t *buffer, int buffer_len, uint64_t *output)
31+
{
32+
int output_index = 0;
33+
int buffer_index = 0;
34+
uint64_t cur = 0;
35+
int cur_count = 0;
36+
while (buffer_index < buffer_len) {
37+
uint8_t byte = buffer[buffer_index++];
38+
cur_count++;
39+
#if SQLite_VARINT == 1
40+
/*
41+
The exercise and test data, as they stand, only handle 32-bit
42+
integers.
43+
This is a specific use case to decode SQLite Varints, which are
44+
64-bit and truncated to 9 bytes when encoded.
45+
When decoding, the 9th byte, if there is one, uses the full 8 bits for
46+
the value.
47+
Finding the 9th byte also marks the end of an integer.
48+
*/
49+
if (cur_count == 9) {
50+
cur <<= 8;
51+
cur |= byte;
52+
output[output_index++] = cur;
53+
cur = 0;
54+
cur_count = 0;
55+
continue;
56+
}
57+
#endif
58+
cur <<= 7;
59+
cur |= byte & 0x7f;
60+
if (!(byte & 0x80)) {
61+
output[output_index++] = cur;
62+
cur = 0;
63+
cur_count = 0;
64+
continue;
65+
}
66+
}
67+
if (cur_count || cur)
68+
return -1;
69+
return output_index;
70+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef VLQ_H
2+
#define VLQ_H
3+
4+
#include <stdint.h>
5+
6+
// return output_len
7+
int encode(uint64_t *integers, int integers_len, uint8_t *output);
8+
9+
// return output_len, -1 on error
10+
int decode(uint8_t *buffer, int buffer_len, uint64_t *output);
11+
12+
#endif
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[35c9db2e-f781-4c52-b73b-8e76427defd0]
13+
description = "Encode a series of integers, producing a series of bytes. -> zero"
14+
15+
[be44d299-a151-4604-a10e-d4b867f41540]
16+
description = "Encode a series of integers, producing a series of bytes. -> arbitrary single byte"
17+
18+
[ea399615-d274-4af6-bbef-a1c23c9e1346]
19+
description = "Encode a series of integers, producing a series of bytes. -> largest single byte"
20+
21+
[77b07086-bd3f-4882-8476-8dcafee79b1c]
22+
description = "Encode a series of integers, producing a series of bytes. -> smallest double byte"
23+
24+
[63955a49-2690-4e22-a556-0040648d6b2d]
25+
description = "Encode a series of integers, producing a series of bytes. -> arbitrary double byte"
26+
27+
[29da7031-0067-43d3-83a7-4f14b29ed97a]
28+
description = "Encode a series of integers, producing a series of bytes. -> largest double byte"
29+
30+
[3345d2e3-79a9-4999-869e-d4856e3a8e01]
31+
description = "Encode a series of integers, producing a series of bytes. -> smallest triple byte"
32+
33+
[5df0bc2d-2a57-4300-a653-a75ee4bd0bee]
34+
description = "Encode a series of integers, producing a series of bytes. -> arbitrary triple byte"
35+
36+
[f51d8539-312d-4db1-945c-250222c6aa22]
37+
description = "Encode a series of integers, producing a series of bytes. -> largest triple byte"
38+
39+
[da78228b-544f-47b7-8bfe-d16b35bbe570]
40+
description = "Encode a series of integers, producing a series of bytes. -> smallest quadruple byte"
41+
42+
[11ed3469-a933-46f1-996f-2231e05d7bb6]
43+
description = "Encode a series of integers, producing a series of bytes. -> arbitrary quadruple byte"
44+
45+
[d5f3f3c3-e0f1-4e7f-aad0-18a44f223d1c]
46+
description = "Encode a series of integers, producing a series of bytes. -> largest quadruple byte"
47+
48+
[91a18b33-24e7-4bfb-bbca-eca78ff4fc47]
49+
description = "Encode a series of integers, producing a series of bytes. -> smallest quintuple byte"
50+
51+
[5f34ff12-2952-4669-95fe-2d11b693d331]
52+
description = "Encode a series of integers, producing a series of bytes. -> arbitrary quintuple byte"
53+
54+
[7489694b-88c3-4078-9864-6fe802411009]
55+
description = "Encode a series of integers, producing a series of bytes. -> maximum 32-bit integer input"
56+
57+
[f9b91821-cada-4a73-9421-3c81d6ff3661]
58+
description = "Encode a series of integers, producing a series of bytes. -> two single-byte values"
59+
60+
[68694449-25d2-4974-ba75-fa7bb36db212]
61+
description = "Encode a series of integers, producing a series of bytes. -> two multi-byte values"
62+
63+
[51a06b5c-de1b-4487-9a50-9db1b8930d85]
64+
description = "Encode a series of integers, producing a series of bytes. -> many multi-byte values"
65+
66+
[baa73993-4514-4915-bac0-f7f585e0e59a]
67+
description = "Decode a series of bytes, producing a series of integers. -> one byte"
68+
69+
[72e94369-29f9-46f2-8c95-6c5b7a595aee]
70+
description = "Decode a series of bytes, producing a series of integers. -> two bytes"
71+
72+
[df5a44c4-56f7-464e-a997-1db5f63ce691]
73+
description = "Decode a series of bytes, producing a series of integers. -> three bytes"
74+
75+
[1bb58684-f2dc-450a-8406-1f3452aa1947]
76+
description = "Decode a series of bytes, producing a series of integers. -> four bytes"
77+
78+
[cecd5233-49f1-4dd1-a41a-9840a40f09cd]
79+
description = "Decode a series of bytes, producing a series of integers. -> maximum 32-bit integer"
80+
81+
[e7d74ba3-8b8e-4bcb-858d-d08302e15695]
82+
description = "Decode a series of bytes, producing a series of integers. -> incomplete sequence causes error"
83+
84+
[aa378291-9043-4724-bc53-aca1b4a3fcb6]
85+
description = "Decode a series of bytes, producing a series of integers. -> incomplete sequence causes error, even if value is zero"
86+
87+
[a91e6f5a-c64a-48e3-8a75-ce1a81e0ebee]
88+
description = "Decode a series of bytes, producing a series of integers. -> multiple values"
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
### If you wish to use extra libraries (math.h for instance),
2+
### add their flags here (-lm in our case) in the "LIBS" variable.
3+
4+
LIBS = -lm
5+
6+
###
7+
CFLAGS = -std=c99
8+
CFLAGS += -g
9+
CFLAGS += -Wall
10+
CFLAGS += -Wextra
11+
CFLAGS += -pedantic
12+
CFLAGS += -Werror
13+
CFLAGS += -Wmissing-declarations
14+
CFLAGS += -DUNITY_SUPPORT_64 -DUNITY_OUTPUT_COLOR
15+
16+
ASANFLAGS = -fsanitize=address
17+
ASANFLAGS += -fno-common
18+
ASANFLAGS += -fno-omit-frame-pointer
19+
20+
.PHONY: test
21+
test: tests.out
22+
@./tests.out
23+
24+
.PHONY: memcheck
25+
memcheck: ./*.c ./*.h
26+
@echo Compiling $@
27+
@$(CC) $(ASANFLAGS) $(CFLAGS) test-framework/unity.c ./*.c -o memcheck.out $(LIBS)
28+
@./memcheck.out
29+
@echo "Memory check passed"
30+
31+
.PHONY: clean
32+
clean:
33+
rm -rf *.o *.out *.out.dSYM
34+
35+
tests.out: ./*.c ./*.h
36+
@echo Compiling $@
37+
@$(CC) $(CFLAGS) test-framework/unity.c ./*.c -o tests.out $(LIBS)

0 commit comments

Comments
 (0)