1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*
* Copyright (c) 2025-2026 Anton Kundenko <singaraiona@gmail.com>
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/* The import dictionary's grouped mode: interning a column too big for its
* dictionary to be probed at random.
*
* A pass over one column runs in three steps, the caller (the Parquet
* import) decoding the column twice:
*
* 1. every task (a row group's chunk) deduplicates its strings exactly and
* hands its distinct ones over as (hash, length, local id) only; no
* string bytes are kept between the steps;
* 2. ray_symgrp_resolve goes through the hash groups one at a time, the
* group's index (every earlier string's hash, length and position, kept
* in append-only logs) the only table it holds, and gives each staged
* string a verdict: a new string at a position of its own, or a
* candidate for an earlier string of equal hash and length;
* 3. the tasks decode again, write the new strings' records where step 2
* placed them and compare every candidate's bytes with its record,
* length first and then bytes to the first that differs. A candidate
* pointing at an earlier pass (or an earlier window of tasks) is
* compared with a copy of its record read beforehand in position
* order (ray_symgrp_load); one pointing at a record another task of the
* window writes is compared once the window is done (ray_symgrp_defer,
* ray_symgrp_settle). A mismatch, a true hash collision, is kept for
* the window's end too (ray_symgrp_mismatch): ray_symgrp_settle gives
* the window's mismatches, in the order their strings first occur, their
* exact positions from the logs, new ones if need be, and their tasks
* write their codes again. Two different strings never share a
* position, and the positions do not depend on the workers.
*
* Nothing reads the symbol file at random but those collisions, and the
* index is touched one group at a time. POSIX only, as the import
* dictionary itself. */
;
typedef struct ray_symgrp_s ray_symgrp_t;
/* The order a pass's new strings take positions in. */
typedef enum ray_symgrp_order_t;
/* A task-distinct string as step 1 saw it; local ids count from 1 (0 is the
* null and "" row, position 0, never staged). */
typedef struct ray_symgrp_fp_t;
/* Step 2's verdict on one of a task's local ids. */
;
typedef struct ray_symgrp_res_t;
typedef struct ray_symgrp_stats_t;
ray_symgrp_t* ;
void ;
ray_symgrp_order_t ;
/* A pass over `ntasks` tasks in `groups` hash groups (a power of two up to
* RAY_SYMGRP_MAX); `workers` bounds the worker ids given to ray_symgrp_defer. */
bool ;
/* Before a pass: the import dictionary's strings added outside the grouped
* passes (by direct ones, which come first) taken into the logs, hashed as
* the stage's strings are (ray_hash_bytes, `hmask` its bits kept). */
bool ;
/* Step 1: task t's distinct strings, fp[i].local == i + 1; counts[i] the
* rows of local id i + 1 (RAY_SYMGRP_FREQ; NULL otherwise). Thread-safe
* across tasks. */
bool ;
/* Step 2. Reserves the new strings' records in the symbol file and appends
* them to the index logs. */
bool ;
/* Step 3: task t's verdicts, res[local] for local ids 1..n (n as staged). */
int64_t ;
bool ;
/* RAY_SYMGRP_ROWS, ROWS_FLAT: the file offset of task t's first new record,
* of those later tasks meet again (`again`) or of the others. */
int64_t ;
/* The window of tasks [ta, return value) whose candidates' records stay
* within `budget` bytes (one task at least). */
int64_t ;
/* Read the records the window's candidates need from before the window, in
* position order, into memory. */
bool ;
/* Whether `s` is the string at `pos`: the loaded copy if any, else the
* record in the file (written by now). */
bool ;
/* A candidate pointing at a record another task of the window writes:
* compared by ray_symgrp_settle. `worker` < the workers of begin. */
bool ;
/* A candidate compared already that did not match: its position is given
* by ray_symgrp_settle. `worker` as for ray_symgrp_defer. */
bool ;
/* After the window: compare the deferred candidates in position order, then
* give every mismatch (those and ray_symgrp_mismatch's), by task and local
* id, its exact position: an earlier string of equal hash and length, or a
* new record. The positions are kept for ray_symgrp_override, and the
* mismatches' tasks listed in *redo (*nredo tasks, ascending) to write their
* codes again. */
bool ;
/* The position settled for (t, local) after a mismatch; -1 if none. */
int64_t ;
/* After the window and its codes written again: start the writeback of the
* records it wrote (and of those its collisions added). */
void ;
/* Step 3's own comparisons, for the counts. */
void ;
/* End of the pass: its state freed, the logs kept for the next. */
void ;
/* Counts since the last call (which resets them). */
void ;
/* Entries in the index logs. */
int64_t ;