Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
seq.c
Go to the documentation of this file.
1/* -*- C -*-
2 * Serene programming language
3 * Copyright (C) 2019-2026 Sameer Rahmani <[email protected]>
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#include "serene/rt/impl/seq.h"
20
21#include <stdint.h>
22#include <stdio.h>
23
24#include "serene/rt/context.h"
25#include "serene/utils.h"
26
27#if defined(SEQ_DEBUG)
28# define SEQ_LOG(FMT, ...) DBG("SEQ", FMT __VA_OPT__(, ) __VA_ARGS__)
29#else
30# define SEQ_LOG(FMT, ...)
31#endif
32/// Find the array index of the given `index`, considering the given `depth`.
33/// depth can be thought of, as which 5 bits of the index are we looking at.
34/// (SEQ_SHIFT is 5).
35static inline uint16_t seq_depth_index(const uint8_t depth, const uint64_t index) {
36 return (uint16_t)((index >> (uint8_t)(depth * SEQ_SHIFT)) & SEQ_MASK);
37}
38
39/// Create a PAGE (just slots for the data)
40static inline seq_elem_t *seq_create_page(const srn_context_t *ctx) {
42 PANIC_IF_NULL((void *)p);
43 for (size_t i = 0; i < SEQ_BR; i++) {
44 p[i] = nullptr;
45 }
46 return p;
47}
48
49static inline seq_node_t *seq_new_node(const srn_context_t *ctx) {
52 n->children = seq_create_page(ctx);
53 return n;
54}
55
56/// Walk from the root to a possible leaf node and create all the inner
57/// and leaf nodes if necessary. We use this only when we insert new data
58static seq_node_t *seq_new_path(const srn_context_t *ctx, uint8_t depth, seq_node_t *leaf) {
59 if (depth == 0) {
60 return leaf;
61 }
62
64 n->children[0] = seq_new_path(ctx, depth - 1, leaf);
65 return n;
66}
67
68/// Clone `n` into a fresh node with its own children page. Slots are copied
69/// by reference, so one slot of the clone can change without touching the
70/// original.
71static seq_node_t *seq_clone_node(const srn_context_t *ctx, const seq_node_t *n) {
72 seq_node_t *c = seq_new_node(ctx);
73 for (size_t i = 0; i < SEQ_BR; i++) {
74 c->children[i] = n->children[i];
75 }
76 return c;
77}
78
79/// Insert `leaf` below `node` at the position of logical index `index`,
80/// cloning every node along the path. Nothing reachable from `node` is
81/// written, so older versions sharing this subtree stay intact.
83 const srn_context_t *ctx, const seq_node_t *node, uint8_t depth, uint64_t index, seq_node_t *leaf
84) {
85 seq_node_t *copy = seq_clone_node(ctx, node);
86 uint16_t idx = seq_depth_index(depth, index);
87
88 if (depth == 1) {
89 copy->children[idx] = (seq_elem_t)leaf;
90 return copy;
91 }
92
93 seq_node_t *child = (seq_node_t *)node->children[idx];
94 copy->children[idx] = (child == nullptr)
95 ? (seq_elem_t)seq_new_path(ctx, depth - 1, leaf)
96 : (seq_elem_t)seq_push_leaf(ctx, child, depth - 1, index, leaf);
97 return copy;
98}
99
101 PANIC_IF_NULL(ctx);
102 seq_t seq;
103 seq.len = 0;
104 seq.tail_len = 0;
105 seq.tail = seq_create_page(ctx);
106 seq.maybe_error = nullptr;
107 seq.root = nullptr;
108 seq.depth = 0;
109 seq.seq_ctx = ctx;
110 return seq;
111}
112
114 const srn_context_t *ctx = seq->seq_ctx;
115 PANIC_IF_NULL(ctx);
116
117 if (seq->len >= SIZE_MAX) {
118 seq_t failed_seq;
119 failed_seq.maybe_error =
120 ERR(ctx, SEQ_LIMIT_REACHED, "Can't fit more elements in this sequence");
121 failed_seq.len = 0;
122 failed_seq.tail_len = 0;
123 failed_seq.depth = 0;
124 failed_seq.root = nullptr;
125 failed_seq.tail = nullptr;
126 failed_seq.seq_ctx = seq->seq_ctx;
127 return failed_seq;
128 }
129
130 if (seq->tail_len < SEQ_BR) {
131 // The tail buffer is shared with every older version of this seq, so
132 // the new version appends into its own copy.
133 seq_t new_seq = *seq;
134 new_seq.maybe_error = nullptr;
135 new_seq.tail = seq_create_page(ctx);
136
137 for (uint16_t i = 0; i < seq->tail_len; i++) {
138 new_seq.tail[i] = seq->tail[i];
139 }
140
141 new_seq.tail[new_seq.tail_len++] = x;
142 new_seq.len++;
143 SEQ_LOG("Tail has space, TL: %d, L: %zu, D: %d", new_seq.tail_len, new_seq.len, new_seq.depth);
144 return new_seq;
145 }
146
147 // elements already in the tree (multiple of SEQ_BR)
148 size_t sz_without_tail = seq->len - (size_t)seq->tail_len;
149 // Build a leaf around the current tail buffer. Sharing the buffer is safe;
150 // it is full, and a push onto any version holding it promotes instead of
151 // writing into it.
152 seq_node_t *leaf = ALLOC(ctx, seq_node_t);
153 leaf->children = seq->tail;
154
155 // Tail buffer is full and we need to to move it (no copy) inside the trie
156 // and allocate a new tail
157 if (seq->root == nullptr) {
158 // This happens only once, at index
159 // (SEQ_BR + 1) and technically we can just
160 seq_t new_seq = *seq;
161 new_seq.maybe_error = nullptr;
162 new_seq.tail_len = 1;
163 new_seq.len++;
164 new_seq.tail = seq_create_page(ctx);
165 new_seq.tail[new_seq.tail_len - 1] = x;
166 new_seq.root = leaf;
167 new_seq.depth = 0;
168 SEQ_LOG("Root is null, TL: %d, L: %zu, D: %d", new_seq.tail_len, new_seq.len, new_seq.depth);
169
170 return new_seq;
171 }
172 // Root is not Null, so we have inner children and we need to find the slot
173 // which we need to move the tail into
174
175 // If the tree is full at current depth, grow a new root
176 if (sz_without_tail >> ((seq->depth + 1) * SEQ_SHIFT)) {
177 // ^^ == floor(sz_without_tail / 2^(5*(d+1)))
178
179 seq_node_t *new_root = seq_new_node(ctx); // internal node
180 new_root->children[0] = seq->root;
181 new_root->children[1] = seq_new_path(ctx, seq->depth, leaf);
182 seq_t new_seq = *seq;
183 new_seq.maybe_error = nullptr;
184 new_seq.root = new_root;
185 new_seq.depth = seq->depth + 1;
186 new_seq.tail = seq_create_page(ctx);
187 new_seq.tail_len = 1;
188 new_seq.len = seq->len + 1;
189 new_seq.tail[0] = x;
190 return new_seq;
191 }
192 // Insert the leaf at the right fringe, cloning the path from the root so
193 // no node shared with older versions is written.
194 seq_t new_seq = *seq;
195 new_seq.maybe_error = nullptr;
196 new_seq.root = seq_push_leaf(ctx, seq->root, seq->depth, (uint64_t)sz_without_tail, leaf);
197 new_seq.tail = seq_create_page(ctx);
198 new_seq.tail_len = 1;
199 new_seq.len = seq->len + 1;
200 new_seq.tail[0] = x;
201 return new_seq;
202}
203
204/// Negative index is not supported
205seq_lookup_result_t seq_get(const seq_t *seq, size_t n) {
206 const srn_context_t *ctx = seq->seq_ctx;
207 PANIC_IF_NULL(ctx);
208 seq_lookup_result_t result = {.data = nullptr, .maybe_error = nullptr};
209
210 if (n >= seq->len) {
211 // Size the message by asking snprintf first; a fixed buffer sized from
212 // sizeof(size_t) counts bytes, not digits, and overflows on large
213 // indices.
214 int msg_len = snprintf(nullptr, 0, "%zu", n);
215 PANIC_IF(msg_len < 0, "index formatting failed");
216 char *err_msg = srn_allocate(ctx, (size_t)msg_len + 1, alignof(char));
217 (void)snprintf(err_msg, (size_t)msg_len + 1, "%zu", n);
218
219 result.maybe_error = ERR(ctx, INDEX_OUT_OF_BOUND, err_msg);
220 result.data = nullptr;
221 return result;
222 }
223
224 size_t tail_start = seq->len - (size_t)seq->tail_len;
225 if (n >= tail_start) {
226 // Tail lookup
227 size_t off = n - tail_start; // 0 .. tail_len-1
228 result.data = seq->tail[off];
229 return result;
230 }
231
232 SEQ_LOG("Looking up in trie: TL: %d, L: %zu N: %zu", seq->tail_len, seq->len, n);
233 seq_node_t *node = seq->root;
234 for (int16_t d = seq->depth; d >= 0; d--) {
235 uint16_t index = seq_depth_index(d, n);
236 SEQ_LOG("Looking up in trie: D: %d, I: %hu", d, index);
237
238 if (d == 0) {
239 SEQ_LOG("We're at depth zero");
240 result.data = node->children[index];
241 return result;
242 }
243
244 // A read must not create nodes. Every index below the tail has a full
245 // path, so a missing child is corruption, not laziness.
246 node = (seq_node_t *)node->children[index];
247 if (!node) {
248 result.maybe_error = ERR(ctx, CORRUPTED_SEQ, "missing child");
249 result.data = nullptr;
250 return result;
251 }
252 }
253
254 PANIC("It should never happen");
255 // just a dummy return
256 result.data = nullptr;
257 result.maybe_error = ERR(ctx, ABSURD, "");
258 return result;
259}
int n
Definition acutest.h:525
void * srn_allocate(const srn_context_t *ctx, size_t size, size_t alignment)
Definition context.c:73
#define ALLOCN(ctx, T, N)
Definition context.h:85
#define ALLOC(ctx, T)
Definition context.h:84
@ CORRUPTED_SEQ
Definition errors.h:79
@ ABSURD
Definition errors.h:76
@ INDEX_OUT_OF_BOUND
Definition errors.h:78
@ SEQ_LIMIT_REACHED
Definition errors.h:77
#define ERR(ctx, err, msg)
Definition errors.h:148
static seq_node_t * seq_new_node(const srn_context_t *ctx)
Definition seq.c:49
static seq_node_t * seq_new_path(const srn_context_t *ctx, uint8_t depth, seq_node_t *leaf)
Walk from the root to a possible leaf node and create all the inner and leaf nodes if necessary.
Definition seq.c:58
seq_t seq_empty(const srn_context_t *ctx)
Create an empty seq pinned to ctx.
Definition seq.c:100
static seq_node_t * seq_clone_node(const srn_context_t *ctx, const seq_node_t *n)
Clone n into a fresh node with its own children page.
Definition seq.c:71
static uint16_t seq_depth_index(const uint8_t depth, const uint64_t index)
Find the array index of the given index, considering the given depth.
Definition seq.c:35
seq_lookup_result_t seq_get(const seq_t *seq, size_t n)
Negative index is not supported.
Definition seq.c:205
static seq_node_t * seq_push_leaf(const srn_context_t *ctx, const seq_node_t *node, uint8_t depth, uint64_t index, seq_node_t *leaf)
Insert leaf below node at the position of logical index index, cloning every node along the path.
Definition seq.c:82
static seq_elem_t * seq_create_page(const srn_context_t *ctx)
Create a PAGE (just slots for the data).
Definition seq.c:40
seq_t seq_push(const seq_t *seq, seq_elem_t x)
Definition seq.c:113
#define SEQ_LOG(FMT,...)
Definition seq.c:30
This is an implementation of bit - partitioned, persistent, immutable sequence For more information,...
#define SEQ_BR
branching factor (power of two)
Definition seq.h:112
#define SEQ_SHIFT
log2(SEQ_BR)
Definition seq.h:115
#define SEQ_MASK
Definition seq.h:116
void * seq_elem_t
We use generic pointers to refer to internal nodes, leaf nodes and even elements.
Definition seq.h:131
seq_elem_t data
Definition seq.h:135
We have two type of node that both are implemented using the same data structure.
Definition seq.h:147
seq_elem_t * children
We allocate children to be a buffer of SEQ_BR number of pointers.
Definition seq.h:149
Definition seq.h:155
size_t len
logical length.
Definition seq.h:161
seq_node_t * root
NULL means “all data is in tail”
Definition seq.h:167
const srn_context_t * seq_ctx
The context that owns every allocation the seq retains.
Definition seq.h:177
seq_elem_t * tail
small tail array for fast push/pop.
Definition seq.h:170
uint8_t depth
tree depth in levels (0 == leaf level)
Definition seq.h:165
uint16_t tail_len
0..SEQ_BR
Definition seq.h:163
#define PANIC_IF_NULL(ptr)
Definition utils.h:66
#define PANIC_IF(cond, msg)
Definition utils.h:59
#define PANIC(msg)
Definition utils.h:53