Skip to content

Commit 836b54d

Browse files
committed
start at C example
1 parent 728b7e6 commit 836b54d

2 files changed

Lines changed: 178 additions & 0 deletions

File tree

c/examples/json_struct_metadata.c

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <err.h>
4+
5+
#include <tskit.h>
6+
7+
#define TSK_JSON_BINARY_HEADER_SIZE 21
8+
9+
static const uint8_t _tsk_json_binary_magic[4] = { 'J', 'B', 'L', 'B' };
10+
11+
/**
12+
This function will "extract" the binary payload from ``json+struct``
13+
encoded metadata, in the sense that it provides pointers to the two
14+
parts (json and struct).
15+
16+
The output pointers reference memory owned by the caller and remain valid only while
17+
the original metadata buffer is alive.
18+
19+
Arguments:
20+
metadata (in): Pointer to the encoded metadata bytes.
21+
metadata_length (in): Number of bytes available at ``metadata``.
22+
json (out): On success, set to the start of the JSON bytes.
23+
json_length (out): On success, set to the JSON length in bytes.
24+
blob (out): On success, set to the start of the binary payload.
25+
blob_length (out): On success, set to the payload length in bytes.
26+
Returns 0 on success or a negative value on failure.
27+
*/
28+
29+
int
30+
tsk_json_struct_metadata_get_blob(char *metadata, tsk_size_t metadata_length,
31+
char **json, tsk_size_t *json_length, char **blob, tsk_size_t *blob_length)
32+
{
33+
int ret;
34+
uint8_t version;
35+
uint64_t json_length_u64;
36+
uint64_t binary_length_u64;
37+
uint64_t header_and_json_length;
38+
uint64_t padding_length;
39+
uint64_t header_and_json_and_padding_length;
40+
uint64_t total_length;
41+
uint8_t *bytes;
42+
char *json_start;
43+
char *padding_start;
44+
char *blob_start;
45+
46+
if (metadata == NULL || json == NULL || json_length == NULL || blob == NULL
47+
|| blob_length == NULL) {
48+
fprintf(stdout, "\nError: Bad parameter value.\n");
49+
ret = -1;
50+
goto out;
51+
}
52+
bytes = (uint8_t *) metadata;
53+
if (metadata_length < TSK_JSON_BINARY_HEADER_SIZE) {
54+
fprintf(stdout, "\nError: Metadata truncated.\n");
55+
ret = -1;
56+
goto out;
57+
}
58+
if (memcmp(bytes, _tsk_json_binary_magic, sizeof(_tsk_json_binary_magic)) != 0) {
59+
fprintf(stdout, "\nError: Bad magic bytes.\n");
60+
ret = -1;
61+
goto out;
62+
}
63+
version = bytes[4];
64+
if (version != 1) {
65+
fprintf(stdout, "\nError: Bad version number.\n");
66+
ret = -1;
67+
goto out;
68+
}
69+
json_length_u64 = tsk_load_u64_le(bytes + 5);
70+
binary_length_u64 = tsk_load_u64_le(bytes + 13);
71+
if (json_length_u64 > UINT64_MAX - (uint64_t) TSK_JSON_BINARY_HEADER_SIZE) {
72+
fprintf(stdout, "\nError: invalid length.\n");
73+
ret = -1;
74+
goto out;
75+
}
76+
header_and_json_length = (uint64_t) TSK_JSON_BINARY_HEADER_SIZE + json_length_u64;
77+
padding_length = (8 - (header_and_json_length & 0x07)) % 8;
78+
header_and_json_and_padding_length = header_and_json_length + padding_length;
79+
if (binary_length_u64 > UINT64_MAX - header_and_json_and_padding_length) {
80+
fprintf(stdout, "\nError: invalid length.\n");
81+
ret = -1;
82+
goto out;
83+
}
84+
total_length = header_and_json_and_padding_length + binary_length_u64;
85+
if ((uint64_t) metadata_length != total_length) {
86+
fprintf(stdout, "\nError: unexpected size.\n");
87+
ret = -1;
88+
goto out;
89+
}
90+
padding_start = (char *) bytes + TSK_JSON_BINARY_HEADER_SIZE + json_length_u64;
91+
for (uint64_t padding_index = 0; padding_index < padding_length; ++padding_index) {
92+
if (*(padding_start + padding_index) != (char) 0) {
93+
fprintf(stdout, "\nError: padding bytes are nonzero.\n");
94+
ret = -1;
95+
goto out;
96+
}
97+
}
98+
json_start = (char *) bytes + TSK_JSON_BINARY_HEADER_SIZE;
99+
blob_start = (char *) bytes + TSK_JSON_BINARY_HEADER_SIZE + json_length_u64
100+
+ padding_length;
101+
*json = json_start;
102+
*json_length = (tsk_size_t) json_length_u64;
103+
*blob = blob_start;
104+
*blob_length = (tsk_size_t) binary_length_u64;
105+
ret = 0;
106+
out:
107+
return ret;
108+
}
109+
110+
int
111+
main(int argc, char **argv)
112+
{
113+
int ret;
114+
char metadata[128];
115+
char *json;
116+
tsk_size_t json_buffer_length;
117+
char *blob;
118+
tsk_size_t blob_length;
119+
uint8_t *bytes;
120+
tsk_size_t metadata_length;
121+
size_t header_length;
122+
size_t json_length;
123+
size_t padding_length;
124+
size_t payload_length;
125+
size_t total_length;
126+
char json_payload[] = "{\"a\":1}";
127+
uint8_t binary_payload[] = { 0x01, 0x02, 0x03, 0x04 };
128+
129+
/* First set up the encoded bytes: */
130+
bytes = (uint8_t *) metadata;
131+
header_length = 4 + 1 + 8 + 8;
132+
json_length = strlen(json_payload);
133+
padding_length = (8 - ((header_length + json_length) & 0x07)) % 8;
134+
payload_length = sizeof(binary_payload);
135+
total_length = header_length + json_length + padding_length + payload_length;
136+
// CU_ASSERT_FATAL(total_length <= sizeof(metadata));
137+
memset(metadata, 0, sizeof(metadata));
138+
bytes[0] = 'J';
139+
bytes[1] = 'B';
140+
bytes[2] = 'L';
141+
bytes[3] = 'B';
142+
bytes[4] = 1;
143+
set_u64_le(bytes + 5, (uint64_t) json_length);
144+
set_u64_le(bytes + 13, (uint64_t) payload_length);
145+
memcpy(bytes + header_length, json_payload, json_length);
146+
memset(bytes + header_length + json_length, 0, padding_length);
147+
memcpy(bytes + header_length + json_length + padding_length, binary_payload,
148+
payload_length);
149+
metadata_length = (tsk_size_t) total_length;
150+
151+
/* Now, we can decode them: */
152+
ret = tsk_json_struct_metadata_get_blob(
153+
metadata, metadata_length, &json, &json_buffer_length, &blob, &blob_length);
154+
155+
/* Next we should print something out to show it worked? */
156+
157+
return ret;
158+
}

docs/c-api.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,3 +949,23 @@ nodes need to be retained, and use
949949
.. literalinclude:: ../c/examples/multichrom_wright_fisher.c
950950
:language: c
951951

952+
----------------------------
953+
Reading and writing metadata
954+
----------------------------
955+
956+
The C API does not provide any functionality for manipulating
957+
the contents of metadata. For JSON metadata it is easy to
958+
parse metadata using an external JSON library, and for
959+
struct-encoded metadata the values can be directly unpacked.
960+
Examples of both can be found in
961+
`the SLiM code <https://messerlab.github.com/slim/>`_.
962+
963+
The :ref:`"json+struct" <sec_metadata_codecs_jsonstruct>`_
964+
metadata codec is a little less straightforward to use,
965+
so we provide here an example of how to write to it
966+
and read from it in C.
967+
968+
.. literalinclude:: ../c/examples/json_struct_metadata.c
969+
:language: c
970+
971+

0 commit comments

Comments
 (0)