Serene Runtime 1.0.0
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/// Get or create the next node of the tree
69static inline seq_node_t *seq_next_child(const srn_context_t *ctx, seq_node_t *n, uint16_t index) {
70 if (n->children[index] == nullptr) {
71 n->children[index] = seq_new_node(ctx);
72 }
73 return n->children[index];
74}
75
77 seq_t seq;
78 seq.len = 0;
79 seq.tail_len = 0;
80 seq.tail = seq_create_page(ctx);
81 seq.maybe_error = nullptr;
82 seq.root = nullptr;
83 seq.depth = 0;
84 return seq;
85}
86
87seq_t seq_push(const srn_context_t *ctx, const seq_t *seq, seq_elem_t x) {
88 if (seq->len >= SIZE_MAX) {
89 seq_t failed_seq;
90 failed_seq.maybe_error =
91 ERR(ctx, SEQ_LIMIT_REACHED, "Can't fit more elements in this sequence");
92 failed_seq.tail_len = 0;
93 failed_seq.len = 0;
94 return failed_seq;
95 }
96
97 if (seq->tail_len < SEQ_BR) {
98 // Tail buffer has enough space
99 seq_t new_seq = *seq;
100 new_seq.maybe_error = nullptr;
101 // new_seq.tail_len++;
102 new_seq.len++;
103 new_seq.tail[new_seq.tail_len++] = x;
104 SEQ_LOG("Tail has space, TL: %d, L: %zu, D: %d", new_seq.tail_len, new_seq.len, new_seq.depth);
105 return new_seq;
106 }
107
108 // elements already in the tree (multiple of SEQ_BR)
109 size_t sz_without_tail = seq->len - (size_t)seq->tail_len;
110 // Build a leaf will own the current tail buffer.
111 seq_node_t *leaf = ALLOC(ctx, seq_node_t);
112 leaf->children = seq->tail;
113
114 // Tail buffer is full and we need to to move it (no copy) inside the trie
115 // and allocate a new tail
116 if (seq->root == nullptr) {
117 // This happens only once, at index
118 // (SEQ_BR + 1) and technically we can just
119 seq_t new_seq = *seq;
120 new_seq.maybe_error = nullptr;
121 new_seq.tail_len = 1;
122 new_seq.len++;
123 new_seq.tail = seq_create_page(ctx);
124 new_seq.tail[new_seq.tail_len - 1] = x;
125 new_seq.root = leaf;
126 new_seq.depth = 0;
127 SEQ_LOG("Root is null, TL: %d, L: %zu, D: %d", new_seq.tail_len, new_seq.len, new_seq.depth);
128
129 return new_seq;
130 }
131 // Root is not Null, so we have inner children and we need to find the slot
132 // which we need to move the tail into
133
134 // If the tree is full at current depth, grow a new root
135 if (sz_without_tail >> ((seq->depth + 1) * SEQ_SHIFT)) {
136 // ^^ == floor(sz_without_tail / 2^(5*(d+1)))
137
138 seq_node_t *new_root = seq_new_node(ctx); // internal node
139 new_root->children[0] = seq->root;
140 new_root->children[1] = seq_new_path(ctx, seq->depth, leaf);
141 seq_t new_seq = *seq;
142 new_seq.root = new_root;
143 new_seq.depth = seq->depth + 1;
144 new_seq.tail = seq_create_page(ctx);
145 new_seq.tail_len = 1;
146 new_seq.len = seq->len + 1;
147 new_seq.tail[0] = x;
148 return new_seq;
149 }
150 // Otherwise insert the leaf at the right fringe under the parent (d==1)
151 seq_node_t *parent = seq->root;
152
153 for (uint8_t d = seq->depth; d > 1; --d) {
154 uint16_t idx = seq_depth_index(d, (uint64_t)sz_without_tail);
155 // ensures internal nodes exist
156 parent = seq_next_child(ctx, parent, idx);
157 }
158
159 // Now at d == 1: use the correct child slot to insert the leaf
160 uint16_t leaf_parent_idx = seq_depth_index(1, (uint64_t)sz_without_tail);
161 parent->children[leaf_parent_idx] = (seq_elem_t)leaf;
162
163 // Start a fresh tail with the pushed element
164 seq_t new_seq = *seq;
165 new_seq.tail = seq_create_page(ctx);
166 new_seq.tail_len = 1;
167 new_seq.len = seq->len + 1;
168 new_seq.tail[0] = x;
169 return new_seq;
170}
171
172/// Negative index is not supported
173seq_lookup_result_t seq_get(const srn_context_t *ctx, const seq_t *seq, size_t n) {
174 seq_lookup_result_t result = {.data = nullptr, .maybe_error = nullptr};
175
176 if (n >= seq->len) {
177 char *err_msg = (char *)ALLOC(ctx, size_t);
178 // +1 is due to null terminated strings
179 SNPRINTF(err_msg, sizeof(size_t) + 1, "%zu", n);
180
181 result.maybe_error = ERR(ctx, INDEX_OUT_OF_BOUND, err_msg);
182 result.data = nullptr;
183 return result;
184 }
185
186 size_t tail_start = seq->len - (size_t)seq->tail_len;
187 if (n >= tail_start) {
188 // Tail lookup
189 size_t off = n - tail_start; // 0 .. tail_len-1
190 result.data = seq->tail[off];
191 return result;
192 }
193
194 SEQ_LOG("Looking up in trie: TL: %d, L: %zu N: %zu", seq->tail_len, seq->len, n);
195 seq_node_t *node = seq->root;
196 for (int16_t d = seq->depth; d >= 0; d--) {
197 uint16_t index = seq_depth_index(d, n);
198 SEQ_LOG("Looking up in trie: D: %d, I: %hu", d, index);
199
200 if (d == 0) {
201 SEQ_LOG("We're at depth zero");
202 result.data = node->children[index];
203 return result;
204 }
205
206 node = seq_next_child(ctx, node, index);
207 if (!node) {
208 result.maybe_error = ERR(ctx, CORRUPTED_SEQ, "missing child");
209 result.data = nullptr;
210 return result;
211 }
212 }
213
214 PANIC("It should never happen");
215 // just a dummy return
216 result.data = nullptr;
217 result.maybe_error = ERR(ctx, ABSURD, "");
218 return result;
219}
int n
Definition acutest.h:538
#define ALLOCN(ctx, T, N)
Definition context.h:83
#define ALLOC(ctx, T)
Definition context.h:82
@ CORRUPTED_SEQ
When the memory layout of a seq is incorrect.
Definition errors.h:48
@ ABSURD
Definition errors.h:45
@ INDEX_OUT_OF_BOUND
Definition errors.h:47
@ SEQ_LIMIT_REACHED
Definition errors.h:46
#define ERR(ctx, err, msg)
Definition errors.h:74
seq_t seq_push(const srn_context_t *ctx, const seq_t *seq, seq_elem_t x)
Definition seq.c:87
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)
Definition seq.c:76
static seq_node_t * seq_next_child(const srn_context_t *ctx, seq_node_t *n, uint16_t index)
Get or create the next node of the tree.
Definition seq.c:69
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 srn_context_t *ctx, const seq_t *seq, size_t n)
Negative index is not supported.
Definition seq.c:173
static seq_elem_t * seq_create_page(const srn_context_t *ctx)
Create a PAGE (just slots for the data)
Definition seq.c:40
#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
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:64
#define SNPRINTF(buf, s, f,...)
Definition utils.h:66
#define PANIC(msg)
Definition utils.h:51