Lite³
A JSON-Compatible Zero-Copy Serialization Format
Loading...
Searching...
No Matches
ctx_api.c
1/*
2 Lite³: A JSON-Compatible Zero-Copy Serialization Format
3
4 Copyright © 2025 Elias de Jong <elias@fastserial.com>
5
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23
24 __ __________________ ____
25 _ ___ ___/ /___(_)_/ /_______|_ /
26 _ _____/ / __/ /_ __/ _ \_/_ <
27 ___ __/ /___/ / / /_ / __/____/
28 /_____/_/ \__/ \___/
29*/
30#include "lite3_context_api.h"
31
32#include <stdio.h>
33#include <string.h>
34#include <stdint.h>
35#include <errno.h>
36#include <assert.h>
37#include <stdlib.h>
38
39
40
41// Typedef for primitive types
42typedef float f32;
43typedef double f64;
44typedef int8_t i8;
45typedef uint8_t u8;
46typedef int16_t i16;
47typedef uint16_t u16;
48typedef int32_t i32;
49typedef uint32_t u32;
50typedef int64_t i64;
51typedef uint64_t u64;
52
53
54
55// If x is already a power of 2, the function returns x
56static inline size_t next_power_of_2(size_t x)
57{
58 x--;
59 x |= x >> 1;
60 x |= x >> 2;
61 x |= x >> 4;
62 x |= x >> 8;
63 x |= x >> 16;
64#if SIZE_MAX == UINT64_MAX // Check if size_t is 64-bit
65 x |= x >> 32;
66#endif
67 x++;
68 return x;
69}
70
71static inline size_t clamp(size_t num, size_t min_val, size_t max_val) {
72 return num < min_val ? min_val : (num > max_val ? max_val : num);
73}
74
76{
77 if (LITE3_UNLIKELY(bufsz > LITE3_BUF_SIZE_MAX)) {
78 LITE3_PRINT_ERROR("INVALID ARGUMENT: bufsz > LITE3_BUF_SIZE_MAX\n");
79 errno = EINVAL;
80 return NULL;
81 }
82 lite3_ctx *ctx = malloc(sizeof(lite3_ctx));
83 if (!ctx)
84 return NULL;
86 ctx->underlying_buf = malloc(bufsz);
87 if (!ctx->underlying_buf) {
88 free(ctx);
89 return NULL;
90 }
91 ctx->buf = (u8 *)(((uintptr_t)ctx->underlying_buf + LITE3_NODE_ALIGNMENT_MASK) & ~LITE3_NODE_ALIGNMENT_MASK);
92 ctx->buflen = 0;
93 ctx->bufsz = (size_t)((uintptr_t)ctx->underlying_buf + (uintptr_t)bufsz - (uintptr_t)ctx->buf);
94 return ctx;
95}
96
97lite3_ctx *lite3_ctx_create_from_buf(const unsigned char *buf, size_t buflen)
98{
99 if (LITE3_UNLIKELY(!(buf && buflen))) {
100 LITE3_PRINT_ERROR("INVALID ARGUMENT: buffer cannot be empty or NULL\n");
101 errno = EINVAL;
102 return NULL;
103 }
104 if (LITE3_UNLIKELY(buflen > LITE3_BUF_SIZE_MAX)) {
105 LITE3_PRINT_ERROR("INVALID ARGUMENT: bufsz > LITE3_BUF_SIZE_MAX\n");
106 errno = EINVAL;
107 return NULL;
108 }
109 size_t new_size = next_power_of_2(buflen + LITE3_NODE_ALIGNMENT_MASK);
110 new_size = new_size == 0 ? LITE3_BUF_SIZE_MAX : new_size;
111 new_size = clamp(new_size, LITE3_CONTEXT_BUF_SIZE_MIN, LITE3_BUF_SIZE_MAX);
112
113 if (LITE3_UNLIKELY(buflen > new_size - LITE3_NODE_ALIGNMENT_MASK)) {
114 LITE3_PRINT_ERROR("NEW SIZE OVERFLOW\n");
115 errno = EOVERFLOW;
116 return NULL;
117 }
118 lite3_ctx *ret = lite3_ctx_create_with_size(new_size);
119 if (ret) {
120 memcpy(ret->buf, buf, buflen);
121 ret->buflen = buflen;
122 }
123 return ret;
124}
125
126lite3_ctx *lite3_ctx_create_take_ownership(unsigned char *buf, size_t buflen, size_t bufsz)
127{
128 if (LITE3_UNLIKELY(!(buf && bufsz))) {
129 LITE3_PRINT_ERROR("INVALID ARGUMENT: buffer cannot be NULL or zero size\n");
130 errno = EINVAL;
131 return NULL;
132 }
133 if (LITE3_UNLIKELY(bufsz > LITE3_BUF_SIZE_MAX)) {
134 LITE3_PRINT_ERROR("INVALID ARGUMENT: bufsz > LITE3_BUF_SIZE_MAX\n");
135 errno = EINVAL;
136 return NULL;
137 }
138 if (LITE3_UNLIKELY(buflen > bufsz)) {
139 LITE3_PRINT_ERROR("INVALID ARGUMENT: buflen > bufsz\n");
140 errno = EINVAL;
141 return NULL;
142 }
143 if (LITE3_UNLIKELY(bufsz < LITE3_CONTEXT_BUF_SIZE_MIN)) {
144 LITE3_PRINT_ERROR("INVALID ARGUMENT: bufsz < LITE3_CONTEXT_BUF_SIZE_MIN\n");
145 errno = EINVAL;
146 return NULL;
147 }
148 if (LITE3_UNLIKELY(((uintptr_t)buf & LITE3_NODE_ALIGNMENT_MASK) != 0)) {
149 LITE3_PRINT_ERROR("INVALID ARGUMENT: *buf not aligned to LITE3_NODE_ALIGNMENT\n");
150 errno = EINVAL;
151 return NULL;
152 }
153 lite3_ctx *ret = malloc(sizeof(lite3_ctx));
154 if (!ret)
155 return NULL;
156 ret->buf = buf;
157 ret->buflen = buflen;
158 ret->bufsz = bufsz;
159 ret->underlying_buf = buf;
160 return ret;
161}
162
163
164int lite3_ctx_grow_impl(lite3_ctx *ctx)
165{
166 if (ctx->bufsz >= LITE3_BUF_SIZE_MAX) {
167 LITE3_PRINT_ERROR("MESSAGE SIZE: bufsz >= LITE3_BUF_SIZE_MAX\n");
168 errno = EMSGSIZE;
169 return -1;
170 }
171 size_t current_size = (size_t)((uintptr_t)ctx->buf - (uintptr_t)ctx->underlying_buf) + ctx->bufsz;
172 size_t new_size = ctx->bufsz < (LITE3_BUF_SIZE_MAX / 4) ? (current_size << 2) : LITE3_BUF_SIZE_MAX; // Increase size by 4X up to LITE3_BUF_SIZE_MAX
173 new_size = clamp(new_size, LITE3_CONTEXT_BUF_SIZE_MIN, LITE3_BUF_SIZE_MAX);
174
175 if (LITE3_UNLIKELY(current_size > new_size - LITE3_NODE_ALIGNMENT_MASK)) {
176 LITE3_PRINT_ERROR("NEW SIZE OVERFLOW\n");
177 errno = EOVERFLOW;
178 return -1;
179 }
180 void *new = malloc(new_size);
181 if (!new)
182 return -1;
183 u8 *new_buf = (u8 *)(((uintptr_t)new + LITE3_NODE_ALIGNMENT_MASK) & ~LITE3_NODE_ALIGNMENT_MASK);
184 memcpy(new_buf, ctx->buf, ctx->buflen);
185 ctx->buf = new_buf;
186 ctx->bufsz = (size_t)((uintptr_t)new + (uintptr_t)new_size - (uintptr_t)new_buf);
187 free(ctx->underlying_buf);
188 ctx->underlying_buf = new;
189 errno = 0;
190 return 0;
191}
192
193int lite3_ctx_import_from_buf(lite3_ctx *ctx, const unsigned char *buf, size_t buflen)
194{
195 if (LITE3_UNLIKELY(!(buf && buflen))) {
196 LITE3_PRINT_ERROR("INVALID ARGUMENT: buffer cannot be empty or NULL\n");
197 errno = EINVAL;
198 return -1;
199 }
200 if (LITE3_UNLIKELY(buflen > LITE3_BUF_SIZE_MAX)) {
201 LITE3_PRINT_ERROR("INVALID ARGUMENT: bufsz > LITE3_BUF_SIZE_MAX\n");
202 errno = EINVAL;
203 return -1;
204 }
205 if (buflen > ctx->bufsz) {
206 size_t new_size = next_power_of_2(buflen + LITE3_NODE_ALIGNMENT_MASK);
207 new_size = new_size == 0 ? LITE3_BUF_SIZE_MAX : new_size;
208 new_size = clamp(new_size, LITE3_CONTEXT_BUF_SIZE_MIN, LITE3_BUF_SIZE_MAX);
209
210 if (LITE3_UNLIKELY(buflen > new_size - LITE3_NODE_ALIGNMENT_MASK)) {
211 LITE3_PRINT_ERROR("NEW SIZE OVERFLOW\n");
212 errno = EOVERFLOW;
213 return -1;
214 }
215 free(ctx->underlying_buf);
216 void *new = malloc(new_size);
217 if (!new)
218 return -1;
219 ctx->underlying_buf = new;
220 u8 *new_buf = (u8 *)(((uintptr_t)new + LITE3_NODE_ALIGNMENT_MASK) & ~LITE3_NODE_ALIGNMENT_MASK);
221 ctx->buf = new_buf;
222 ctx->bufsz = (size_t)((uintptr_t)new + (uintptr_t)new_size - (uintptr_t)new_buf);
223 }
224 ctx->buflen = buflen;
225 memcpy(ctx->buf, buf, buflen);
226 return 0;
227}
228
230{
231 free(ctx->underlying_buf);
232 ctx->buf = NULL;
233 ctx->underlying_buf = NULL;
234 free(ctx);
235}
#define LITE3_BUF_SIZE_MAX
Maximum Lite³ buffer size.
Definition lite3.h:259
#define LITE3_CONTEXT_BUF_SIZE_MIN
The minimum buffer size for a Lite³ context.
lite3_ctx * lite3_ctx_create_with_size(size_t bufsz)
Create context with custom size.
Definition ctx_api.c:75
void lite3_ctx_destroy(lite3_ctx *ctx)
Destroy context.
Definition ctx_api.c:229
lite3_ctx * lite3_ctx_create_take_ownership(unsigned char *buf, size_t buflen, size_t bufsz)
Create context by taking ownership of a buffer.
Definition ctx_api.c:126
lite3_ctx * lite3_ctx_create_from_buf(const unsigned char *buf, size_t buflen)
Create context by copying from a buffer.
Definition ctx_api.c:97
int lite3_ctx_import_from_buf(lite3_ctx *ctx, const unsigned char *buf, size_t buflen)
Copy data into existing context.
Definition ctx_api.c:193
Lite³ Context API Header.
Lite³ context struct.