1d4afb5ceSopenharmony_ci /*
2d4afb5ceSopenharmony_ci * libwebsockets - small server side websockets and web server implementation
3d4afb5ceSopenharmony_ci *
4d4afb5ceSopenharmony_ci * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5d4afb5ceSopenharmony_ci *
6d4afb5ceSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
7d4afb5ceSopenharmony_ci * of this software and associated documentation files (the "Software"), to
8d4afb5ceSopenharmony_ci * deal in the Software without restriction, including without limitation the
9d4afb5ceSopenharmony_ci * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10d4afb5ceSopenharmony_ci * sell copies of the Software, and to permit persons to whom the Software is
11d4afb5ceSopenharmony_ci * furnished to do so, subject to the following conditions:
12d4afb5ceSopenharmony_ci *
13d4afb5ceSopenharmony_ci * The above copyright notice and this permission notice shall be included in
14d4afb5ceSopenharmony_ci * all copies or substantial portions of the Software.
15d4afb5ceSopenharmony_ci *
16d4afb5ceSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17d4afb5ceSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18d4afb5ceSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19d4afb5ceSopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20d4afb5ceSopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21d4afb5ceSopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22d4afb5ceSopenharmony_ci * IN THE SOFTWARE.
23d4afb5ceSopenharmony_ci *
24d4afb5ceSopenharmony_ci * The functions allow
25d4afb5ceSopenharmony_ci *
26d4afb5ceSopenharmony_ci *  - collecting a concordance of strings from one or more files (eg, a
27d4afb5ceSopenharmony_ci *    directory of files) into a single in-memory, lac-backed trie;
28d4afb5ceSopenharmony_ci *
29d4afb5ceSopenharmony_ci *  - to optimize and serialize the in-memory trie to an fd;
30d4afb5ceSopenharmony_ci *
31d4afb5ceSopenharmony_ci *  - to very quickly report any instances of a string in any of the files
32d4afb5ceSopenharmony_ci *    indexed by the trie, by a seeking around a serialized trie fd, without
33d4afb5ceSopenharmony_ci *    having to load it all in memory
34d4afb5ceSopenharmony_ci */
35d4afb5ceSopenharmony_ci
36d4afb5ceSopenharmony_ci#include "private-lib-core.h"
37d4afb5ceSopenharmony_ci#include "private-lib-misc-fts.h"
38d4afb5ceSopenharmony_ci
39d4afb5ceSopenharmony_ci#include <stdio.h>
40d4afb5ceSopenharmony_ci#include <string.h>
41d4afb5ceSopenharmony_ci#include <assert.h>
42d4afb5ceSopenharmony_ci#include <fcntl.h>
43d4afb5ceSopenharmony_ci#include <errno.h>
44d4afb5ceSopenharmony_ci#include <sys/types.h>
45d4afb5ceSopenharmony_ci
46d4afb5ceSopenharmony_cistruct lws_fts_entry;
47d4afb5ceSopenharmony_ci
48d4afb5ceSopenharmony_ci/* notice these are stored in t->lwsac_input_head which has input file scope */
49d4afb5ceSopenharmony_ci
50d4afb5ceSopenharmony_cistruct lws_fts_filepath {
51d4afb5ceSopenharmony_ci	struct lws_fts_filepath *next;
52d4afb5ceSopenharmony_ci	struct lws_fts_filepath *prev;
53d4afb5ceSopenharmony_ci	char filepath[256];
54d4afb5ceSopenharmony_ci	jg2_file_offset ofs;
55d4afb5ceSopenharmony_ci	jg2_file_offset line_table_ofs;
56d4afb5ceSopenharmony_ci	int filepath_len;
57d4afb5ceSopenharmony_ci	int file_index;
58d4afb5ceSopenharmony_ci	int total_lines;
59d4afb5ceSopenharmony_ci	int priority;
60d4afb5ceSopenharmony_ci};
61d4afb5ceSopenharmony_ci
62d4afb5ceSopenharmony_ci/* notice these are stored in t->lwsac_input_head which has input file scope */
63d4afb5ceSopenharmony_ci
64d4afb5ceSopenharmony_cistruct lws_fts_lines {
65d4afb5ceSopenharmony_ci	struct lws_fts_lines *lines_next;
66d4afb5ceSopenharmony_ci	/*
67d4afb5ceSopenharmony_ci	 * amount of line numbers needs to meet average count for best
68d4afb5ceSopenharmony_ci	 * efficiency.
69d4afb5ceSopenharmony_ci	 *
70d4afb5ceSopenharmony_ci	 * Line numbers are stored in VLI format since if we don't, around half
71d4afb5ceSopenharmony_ci	 * the total lac allocation consists of struct lws_fts_lines...
72d4afb5ceSopenharmony_ci	 * size chosen to maintain 8-byte struct alignment
73d4afb5ceSopenharmony_ci	 */
74d4afb5ceSopenharmony_ci	uint8_t vli[119];
75d4afb5ceSopenharmony_ci	char count;
76d4afb5ceSopenharmony_ci};
77d4afb5ceSopenharmony_ci
78d4afb5ceSopenharmony_ci/* this represents the instances of a symbol inside a given filepath */
79d4afb5ceSopenharmony_ci
80d4afb5ceSopenharmony_cistruct lws_fts_instance_file {
81d4afb5ceSopenharmony_ci	/* linked-list of tifs generated for current file */
82d4afb5ceSopenharmony_ci	struct lws_fts_instance_file *inst_file_next;
83d4afb5ceSopenharmony_ci	struct lws_fts_entry *owner;
84d4afb5ceSopenharmony_ci	struct lws_fts_lines *lines_list, *lines_tail;
85d4afb5ceSopenharmony_ci	uint32_t file_index;
86d4afb5ceSopenharmony_ci	uint32_t total;
87d4afb5ceSopenharmony_ci
88d4afb5ceSopenharmony_ci	/*
89d4afb5ceSopenharmony_ci	 * optimization for the common case there's only 1 - ~3 matches, so we
90d4afb5ceSopenharmony_ci	 * don't have to allocate any lws_fts_lines struct
91d4afb5ceSopenharmony_ci	 *
92d4afb5ceSopenharmony_ci	 * Using 8 bytes total for this maintains 8-byte struct alignment...
93d4afb5ceSopenharmony_ci	 */
94d4afb5ceSopenharmony_ci
95d4afb5ceSopenharmony_ci	uint8_t vli[7];
96d4afb5ceSopenharmony_ci	char count;
97d4afb5ceSopenharmony_ci};
98d4afb5ceSopenharmony_ci
99d4afb5ceSopenharmony_ci/*
100d4afb5ceSopenharmony_ci * this is the main trie in-memory allocation object
101d4afb5ceSopenharmony_ci */
102d4afb5ceSopenharmony_ci
103d4afb5ceSopenharmony_cistruct lws_fts_entry {
104d4afb5ceSopenharmony_ci	struct lws_fts_entry *parent;
105d4afb5ceSopenharmony_ci
106d4afb5ceSopenharmony_ci	struct lws_fts_entry *child_list;
107d4afb5ceSopenharmony_ci	struct lws_fts_entry *sibling;
108d4afb5ceSopenharmony_ci
109d4afb5ceSopenharmony_ci	/*
110d4afb5ceSopenharmony_ci	 * care... this points to content in t->lwsac_input_head, it goes
111d4afb5ceSopenharmony_ci	 * out of scope when the input file being indexed completes
112d4afb5ceSopenharmony_ci	 */
113d4afb5ceSopenharmony_ci	struct lws_fts_instance_file *inst_file_list;
114d4afb5ceSopenharmony_ci
115d4afb5ceSopenharmony_ci	jg2_file_offset ofs_last_inst_file;
116d4afb5ceSopenharmony_ci
117d4afb5ceSopenharmony_ci	char *suffix; /* suffix string or NULL if one char (in .c) */
118d4afb5ceSopenharmony_ci	jg2_file_offset ofs;
119d4afb5ceSopenharmony_ci	uint32_t child_count;
120d4afb5ceSopenharmony_ci	uint32_t instance_count;
121d4afb5ceSopenharmony_ci	uint32_t agg_inst_count;
122d4afb5ceSopenharmony_ci	uint32_t agg_child_count;
123d4afb5ceSopenharmony_ci	uint32_t suffix_len;
124d4afb5ceSopenharmony_ci	unsigned char c;
125d4afb5ceSopenharmony_ci};
126d4afb5ceSopenharmony_ci
127d4afb5ceSopenharmony_ci/* there's only one of these per trie file */
128d4afb5ceSopenharmony_ci
129d4afb5ceSopenharmony_cistruct lws_fts {
130d4afb5ceSopenharmony_ci	struct lwsac *lwsac_head;
131d4afb5ceSopenharmony_ci	struct lwsac *lwsac_input_head;
132d4afb5ceSopenharmony_ci	struct lws_fts_entry *root;
133d4afb5ceSopenharmony_ci	struct lws_fts_filepath *filepath_list;
134d4afb5ceSopenharmony_ci	struct lws_fts_filepath *fp;
135d4afb5ceSopenharmony_ci
136d4afb5ceSopenharmony_ci	struct lws_fts_entry *parser;
137d4afb5ceSopenharmony_ci	struct lws_fts_entry *root_lookup[256];
138d4afb5ceSopenharmony_ci
139d4afb5ceSopenharmony_ci	/*
140d4afb5ceSopenharmony_ci	 * head of linked-list of tifs generated for current file
141d4afb5ceSopenharmony_ci	 * care... this points to content in t->lwsac_input_head
142d4afb5ceSopenharmony_ci	 */
143d4afb5ceSopenharmony_ci	struct lws_fts_instance_file *tif_list;
144d4afb5ceSopenharmony_ci
145d4afb5ceSopenharmony_ci	jg2_file_offset c; /* length of output file so far */
146d4afb5ceSopenharmony_ci
147d4afb5ceSopenharmony_ci	uint64_t agg_trie_creation_us;
148d4afb5ceSopenharmony_ci	uint64_t agg_raw_input;
149d4afb5ceSopenharmony_ci	uint64_t worst_lwsac_input_size;
150d4afb5ceSopenharmony_ci	int last_file_index;
151d4afb5ceSopenharmony_ci	int chars_in_line;
152d4afb5ceSopenharmony_ci	jg2_file_offset last_block_len_ofs;
153d4afb5ceSopenharmony_ci	int line_number;
154d4afb5ceSopenharmony_ci	int lines_in_unsealed_linetable;
155d4afb5ceSopenharmony_ci	int next_file_index;
156d4afb5ceSopenharmony_ci	int count_entries;
157d4afb5ceSopenharmony_ci
158d4afb5ceSopenharmony_ci	int fd;
159d4afb5ceSopenharmony_ci	unsigned int agg_pos;
160d4afb5ceSopenharmony_ci	unsigned int str_match_pos;
161d4afb5ceSopenharmony_ci
162d4afb5ceSopenharmony_ci	unsigned char aggregate;
163d4afb5ceSopenharmony_ci	unsigned char agg[128];
164d4afb5ceSopenharmony_ci};
165d4afb5ceSopenharmony_ci
166d4afb5ceSopenharmony_ci/* since the kernel case allocates >300MB, no point keeping this too low */
167d4afb5ceSopenharmony_ci
168d4afb5ceSopenharmony_ci#define TRIE_LWSAC_BLOCK_SIZE (1024 * 1024)
169d4afb5ceSopenharmony_ci
170d4afb5ceSopenharmony_ci#define spill(margin, force) \
171d4afb5ceSopenharmony_ci	if (bp && ((uint32_t)bp >= (sizeof(buf) - (size_t)(margin)) || (force))) { \
172d4afb5ceSopenharmony_ci		if ((int)write(t->fd, buf, (size_t)bp) != bp) { \
173d4afb5ceSopenharmony_ci			lwsl_err("%s: write %d failed (%d)\n", __func__, \
174d4afb5ceSopenharmony_ci				 bp, errno); \
175d4afb5ceSopenharmony_ci			return 1; \
176d4afb5ceSopenharmony_ci		} \
177d4afb5ceSopenharmony_ci		t->c += (unsigned int)bp; \
178d4afb5ceSopenharmony_ci		bp = 0; \
179d4afb5ceSopenharmony_ci	}
180d4afb5ceSopenharmony_ci
181d4afb5ceSopenharmony_cistatic int
182d4afb5ceSopenharmony_cig32(unsigned char *b, uint32_t d)
183d4afb5ceSopenharmony_ci{
184d4afb5ceSopenharmony_ci	*b++ = (uint8_t)((d >> 24) & 0xff);
185d4afb5ceSopenharmony_ci	*b++ = (uint8_t)((d >> 16) & 0xff);
186d4afb5ceSopenharmony_ci	*b++ = (uint8_t)((d >> 8) & 0xff);
187d4afb5ceSopenharmony_ci	*b = (uint8_t)(d & 0xff);
188d4afb5ceSopenharmony_ci
189d4afb5ceSopenharmony_ci	return 4;
190d4afb5ceSopenharmony_ci}
191d4afb5ceSopenharmony_ci
192d4afb5ceSopenharmony_cistatic int
193d4afb5ceSopenharmony_cig16(unsigned char *b, int d)
194d4afb5ceSopenharmony_ci{
195d4afb5ceSopenharmony_ci	*b++ = (uint8_t)((d >> 8) & 0xff);
196d4afb5ceSopenharmony_ci	*b = (uint8_t)(d & 0xff);
197d4afb5ceSopenharmony_ci
198d4afb5ceSopenharmony_ci	return 2;
199d4afb5ceSopenharmony_ci}
200d4afb5ceSopenharmony_ci
201d4afb5ceSopenharmony_cistatic int
202d4afb5ceSopenharmony_ciwq32(unsigned char *b, uint32_t d)
203d4afb5ceSopenharmony_ci{
204d4afb5ceSopenharmony_ci	unsigned char *ob = b;
205d4afb5ceSopenharmony_ci
206d4afb5ceSopenharmony_ci	if (d > (1 << 28) - 1)
207d4afb5ceSopenharmony_ci		*b++ = (uint8_t)(((d >> 28) | 0x80) & 0xff);
208d4afb5ceSopenharmony_ci
209d4afb5ceSopenharmony_ci	if (d > (1 << 21) - 1)
210d4afb5ceSopenharmony_ci		*b++ = (uint8_t)(((d >> 21) | 0x80) & 0xff);
211d4afb5ceSopenharmony_ci
212d4afb5ceSopenharmony_ci	if (d > (1 << 14) - 1)
213d4afb5ceSopenharmony_ci		*b++ = (uint8_t)(((d >> 14) | 0x80) & 0xff);
214d4afb5ceSopenharmony_ci
215d4afb5ceSopenharmony_ci	if (d > (1 << 7) - 1)
216d4afb5ceSopenharmony_ci		*b++ = (uint8_t)(((d >> 7) | 0x80) & 0xff);
217d4afb5ceSopenharmony_ci
218d4afb5ceSopenharmony_ci	*b++ = (uint8_t)(d & 0x7f);
219d4afb5ceSopenharmony_ci
220d4afb5ceSopenharmony_ci	return lws_ptr_diff(b, ob);
221d4afb5ceSopenharmony_ci}
222d4afb5ceSopenharmony_ci
223d4afb5ceSopenharmony_ci
224d4afb5ceSopenharmony_ci/* read a VLI, return the number of bytes used */
225d4afb5ceSopenharmony_ci
226d4afb5ceSopenharmony_ciint
227d4afb5ceSopenharmony_cirq32(unsigned char *b, uint32_t *d)
228d4afb5ceSopenharmony_ci{
229d4afb5ceSopenharmony_ci	unsigned char *ob = b;
230d4afb5ceSopenharmony_ci	uint32_t t = 0;
231d4afb5ceSopenharmony_ci
232d4afb5ceSopenharmony_ci	t = *b & 0x7f;
233d4afb5ceSopenharmony_ci	if (*(b++) & 0x80) {
234d4afb5ceSopenharmony_ci		t = (t << 7) | (*b & 0x7f);
235d4afb5ceSopenharmony_ci		if (*(b++) & 0x80) {
236d4afb5ceSopenharmony_ci			t = (t << 7) | (*b & 0x7f);
237d4afb5ceSopenharmony_ci			if (*(b++) & 0x80) {
238d4afb5ceSopenharmony_ci				t = (t << 7) | (*b & 0x7f);
239d4afb5ceSopenharmony_ci				if (*(b++) & 0x80) {
240d4afb5ceSopenharmony_ci					t = (t << 7) | (*b & 0x7f);
241d4afb5ceSopenharmony_ci					b++;
242d4afb5ceSopenharmony_ci				}
243d4afb5ceSopenharmony_ci			}
244d4afb5ceSopenharmony_ci		}
245d4afb5ceSopenharmony_ci	}
246d4afb5ceSopenharmony_ci
247d4afb5ceSopenharmony_ci	*d = t;
248d4afb5ceSopenharmony_ci
249d4afb5ceSopenharmony_ci	return (int)(b - ob);
250d4afb5ceSopenharmony_ci}
251d4afb5ceSopenharmony_ci
252d4afb5ceSopenharmony_cistruct lws_fts *
253d4afb5ceSopenharmony_cilws_fts_create(int fd)
254d4afb5ceSopenharmony_ci{
255d4afb5ceSopenharmony_ci	struct lws_fts *t;
256d4afb5ceSopenharmony_ci	struct lwsac *lwsac_head = NULL;
257d4afb5ceSopenharmony_ci	unsigned char buf[TRIE_FILE_HDR_SIZE];
258d4afb5ceSopenharmony_ci
259d4afb5ceSopenharmony_ci	t = lwsac_use(&lwsac_head, sizeof(*t), TRIE_LWSAC_BLOCK_SIZE);
260d4afb5ceSopenharmony_ci	if (!t)
261d4afb5ceSopenharmony_ci		return NULL;
262d4afb5ceSopenharmony_ci
263d4afb5ceSopenharmony_ci	memset(t, 0, sizeof(*t));
264d4afb5ceSopenharmony_ci
265d4afb5ceSopenharmony_ci	t->fd = fd;
266d4afb5ceSopenharmony_ci	t->lwsac_head = lwsac_head;
267d4afb5ceSopenharmony_ci	t->root = lwsac_use(&lwsac_head, sizeof(*t->root),
268d4afb5ceSopenharmony_ci			    TRIE_LWSAC_BLOCK_SIZE);
269d4afb5ceSopenharmony_ci	if (!t->root)
270d4afb5ceSopenharmony_ci		goto unwind;
271d4afb5ceSopenharmony_ci
272d4afb5ceSopenharmony_ci	memset(t->root, 0, sizeof(*t->root));
273d4afb5ceSopenharmony_ci	t->parser = t->root;
274d4afb5ceSopenharmony_ci	t->last_file_index = -1;
275d4afb5ceSopenharmony_ci	t->line_number = 1;
276d4afb5ceSopenharmony_ci	t->filepath_list = NULL;
277d4afb5ceSopenharmony_ci
278d4afb5ceSopenharmony_ci	memset(t->root_lookup, 0, sizeof(*t->root_lookup));
279d4afb5ceSopenharmony_ci
280d4afb5ceSopenharmony_ci	/* write the header */
281d4afb5ceSopenharmony_ci
282d4afb5ceSopenharmony_ci	buf[0] = 0xca;
283d4afb5ceSopenharmony_ci	buf[1] = 0x7a;
284d4afb5ceSopenharmony_ci	buf[2] = 0x5f;
285d4afb5ceSopenharmony_ci	buf[3] = 0x75;
286d4afb5ceSopenharmony_ci
287d4afb5ceSopenharmony_ci	/* (these are filled in with correct data at the end) */
288d4afb5ceSopenharmony_ci
289d4afb5ceSopenharmony_ci	/* file offset to root trie entry */
290d4afb5ceSopenharmony_ci	g32(&buf[4], 0);
291d4afb5ceSopenharmony_ci	/* file length when it was created */
292d4afb5ceSopenharmony_ci	g32(&buf[8], 0);
293d4afb5ceSopenharmony_ci	/* fileoffset to the filepath table */
294d4afb5ceSopenharmony_ci	g32(&buf[0xc], 0);
295d4afb5ceSopenharmony_ci	/* count of filepaths */
296d4afb5ceSopenharmony_ci	g32(&buf[0x10], 0);
297d4afb5ceSopenharmony_ci
298d4afb5ceSopenharmony_ci	if (write(t->fd, buf, TRIE_FILE_HDR_SIZE) != TRIE_FILE_HDR_SIZE) {
299d4afb5ceSopenharmony_ci		lwsl_err("%s: trie header write failed\n", __func__);
300d4afb5ceSopenharmony_ci		goto unwind;
301d4afb5ceSopenharmony_ci	}
302d4afb5ceSopenharmony_ci
303d4afb5ceSopenharmony_ci	t->c = TRIE_FILE_HDR_SIZE;
304d4afb5ceSopenharmony_ci
305d4afb5ceSopenharmony_ci	return t;
306d4afb5ceSopenharmony_ci
307d4afb5ceSopenharmony_ciunwind:
308d4afb5ceSopenharmony_ci	lwsac_free(&lwsac_head);
309d4afb5ceSopenharmony_ci
310d4afb5ceSopenharmony_ci	return NULL;
311d4afb5ceSopenharmony_ci}
312d4afb5ceSopenharmony_ci
313d4afb5ceSopenharmony_civoid
314d4afb5ceSopenharmony_cilws_fts_destroy(struct lws_fts **trie)
315d4afb5ceSopenharmony_ci{
316d4afb5ceSopenharmony_ci	struct lwsac *lwsac_head = (*trie)->lwsac_head;
317d4afb5ceSopenharmony_ci	lwsac_free(&(*trie)->lwsac_input_head);
318d4afb5ceSopenharmony_ci	lwsac_free(&lwsac_head);
319d4afb5ceSopenharmony_ci	*trie = NULL;
320d4afb5ceSopenharmony_ci}
321d4afb5ceSopenharmony_ci
322d4afb5ceSopenharmony_ciint
323d4afb5ceSopenharmony_cilws_fts_file_index(struct lws_fts *t, const char *filepath, int filepath_len,
324d4afb5ceSopenharmony_ci		    int priority)
325d4afb5ceSopenharmony_ci{
326d4afb5ceSopenharmony_ci	struct lws_fts_filepath *fp = t->filepath_list;
327d4afb5ceSopenharmony_ci#if 0
328d4afb5ceSopenharmony_ci	while (fp) {
329d4afb5ceSopenharmony_ci		if (fp->filepath_len == filepath_len &&
330d4afb5ceSopenharmony_ci		    !strcmp(fp->filepath, filepath))
331d4afb5ceSopenharmony_ci			return fp->file_index;
332d4afb5ceSopenharmony_ci
333d4afb5ceSopenharmony_ci		fp = fp->next;
334d4afb5ceSopenharmony_ci	}
335d4afb5ceSopenharmony_ci#endif
336d4afb5ceSopenharmony_ci	fp = lwsac_use(&t->lwsac_head, sizeof(*fp), TRIE_LWSAC_BLOCK_SIZE);
337d4afb5ceSopenharmony_ci	if (!fp)
338d4afb5ceSopenharmony_ci		return -1;
339d4afb5ceSopenharmony_ci
340d4afb5ceSopenharmony_ci	fp->next = t->filepath_list;
341d4afb5ceSopenharmony_ci	t->filepath_list = fp;
342d4afb5ceSopenharmony_ci	strncpy(fp->filepath, filepath, sizeof(fp->filepath) - 1);
343d4afb5ceSopenharmony_ci	fp->filepath[sizeof(fp->filepath) - 1] = '\0';
344d4afb5ceSopenharmony_ci	fp->filepath_len = filepath_len;
345d4afb5ceSopenharmony_ci	fp->file_index = t->next_file_index++;
346d4afb5ceSopenharmony_ci	fp->line_table_ofs = t->c;
347d4afb5ceSopenharmony_ci	fp->priority = priority;
348d4afb5ceSopenharmony_ci	fp->total_lines = 0;
349d4afb5ceSopenharmony_ci	t->fp = fp;
350d4afb5ceSopenharmony_ci
351d4afb5ceSopenharmony_ci	return fp->file_index;
352d4afb5ceSopenharmony_ci}
353d4afb5ceSopenharmony_ci
354d4afb5ceSopenharmony_cistatic struct lws_fts_entry *
355d4afb5ceSopenharmony_cilws_fts_entry_child_add(struct lws_fts *t, unsigned char c,
356d4afb5ceSopenharmony_ci			struct lws_fts_entry *parent)
357d4afb5ceSopenharmony_ci{
358d4afb5ceSopenharmony_ci	struct lws_fts_entry *e, **pe;
359d4afb5ceSopenharmony_ci
360d4afb5ceSopenharmony_ci	e = lwsac_use(&t->lwsac_head, sizeof(*e), TRIE_LWSAC_BLOCK_SIZE);
361d4afb5ceSopenharmony_ci	if (!e)
362d4afb5ceSopenharmony_ci		return NULL;
363d4afb5ceSopenharmony_ci
364d4afb5ceSopenharmony_ci	memset(e, 0, sizeof(*e));
365d4afb5ceSopenharmony_ci
366d4afb5ceSopenharmony_ci	e->c = c;
367d4afb5ceSopenharmony_ci	parent->child_count++;
368d4afb5ceSopenharmony_ci	e->parent = parent;
369d4afb5ceSopenharmony_ci	t->count_entries++;
370d4afb5ceSopenharmony_ci
371d4afb5ceSopenharmony_ci	/* keep the parent child list in ascending sort order for c */
372d4afb5ceSopenharmony_ci
373d4afb5ceSopenharmony_ci	pe = &parent->child_list;
374d4afb5ceSopenharmony_ci	while (*pe) {
375d4afb5ceSopenharmony_ci		assert((*pe)->parent == parent);
376d4afb5ceSopenharmony_ci		if ((*pe)->c > c) {
377d4afb5ceSopenharmony_ci			/* add it before */
378d4afb5ceSopenharmony_ci			e->sibling = *pe;
379d4afb5ceSopenharmony_ci			*pe = e;
380d4afb5ceSopenharmony_ci			break;
381d4afb5ceSopenharmony_ci		}
382d4afb5ceSopenharmony_ci		pe = &(*pe)->sibling;
383d4afb5ceSopenharmony_ci	}
384d4afb5ceSopenharmony_ci
385d4afb5ceSopenharmony_ci	if (!*pe) {
386d4afb5ceSopenharmony_ci		/* add it at the end */
387d4afb5ceSopenharmony_ci		e->sibling = NULL;
388d4afb5ceSopenharmony_ci		*pe = e;
389d4afb5ceSopenharmony_ci	}
390d4afb5ceSopenharmony_ci
391d4afb5ceSopenharmony_ci	return e;
392d4afb5ceSopenharmony_ci}
393d4afb5ceSopenharmony_ci
394d4afb5ceSopenharmony_cistatic int
395d4afb5ceSopenharmony_cifinalize_per_input(struct lws_fts *t)
396d4afb5ceSopenharmony_ci{
397d4afb5ceSopenharmony_ci	struct lws_fts_instance_file *tif;
398d4afb5ceSopenharmony_ci	unsigned char buf[8192];
399d4afb5ceSopenharmony_ci	uint64_t lwsac_input_size;
400d4afb5ceSopenharmony_ci	jg2_file_offset temp;
401d4afb5ceSopenharmony_ci	int bp = 0;
402d4afb5ceSopenharmony_ci
403d4afb5ceSopenharmony_ci	bp += g16(&buf[bp], 0);
404d4afb5ceSopenharmony_ci	bp += g16(&buf[bp], 0);
405d4afb5ceSopenharmony_ci	bp += g32(&buf[bp], 0);
406d4afb5ceSopenharmony_ci	if ((int)write(t->fd, buf, (size_t)bp) != bp)
407d4afb5ceSopenharmony_ci		return 1;
408d4afb5ceSopenharmony_ci	t->c += (unsigned int)bp;
409d4afb5ceSopenharmony_ci	bp = 0;
410d4afb5ceSopenharmony_ci
411d4afb5ceSopenharmony_ci	/*
412d4afb5ceSopenharmony_ci	 * Write the generated file index + instances (if any)
413d4afb5ceSopenharmony_ci	 *
414d4afb5ceSopenharmony_ci	 * Notice the next same-parent file instance fileoffset list is
415d4afb5ceSopenharmony_ci	 * backwards, so it does not require seeks to fill in.  The first
416d4afb5ceSopenharmony_ci	 * entry has 0 but the second entry points to the first entry (whose
417d4afb5ceSopenharmony_ci	 * fileoffset is known).
418d4afb5ceSopenharmony_ci	 *
419d4afb5ceSopenharmony_ci	 * After all the file instance structs are finalized,
420d4afb5ceSopenharmony_ci	 * .ofs_last_inst_file contains the fileoffset of that child's tif
421d4afb5ceSopenharmony_ci	 * list head in the file.
422d4afb5ceSopenharmony_ci	 *
423d4afb5ceSopenharmony_ci	 * The file instances are written to disk in the order that the files
424d4afb5ceSopenharmony_ci	 * were indexed, along with their prev pointers inline.
425d4afb5ceSopenharmony_ci	 */
426d4afb5ceSopenharmony_ci
427d4afb5ceSopenharmony_ci	tif = t->tif_list;
428d4afb5ceSopenharmony_ci	while (tif) {
429d4afb5ceSopenharmony_ci		struct lws_fts_lines *i;
430d4afb5ceSopenharmony_ci
431d4afb5ceSopenharmony_ci		spill((3 * MAX_VLI) + tif->count, 0);
432d4afb5ceSopenharmony_ci
433d4afb5ceSopenharmony_ci		temp = tif->owner->ofs_last_inst_file;
434d4afb5ceSopenharmony_ci		if (tif->total)
435d4afb5ceSopenharmony_ci			tif->owner->ofs_last_inst_file = t->c + (unsigned int)bp;
436d4afb5ceSopenharmony_ci
437d4afb5ceSopenharmony_ci		assert(!temp || (temp > TRIE_FILE_HDR_SIZE && temp < t->c));
438d4afb5ceSopenharmony_ci
439d4afb5ceSopenharmony_ci		/* fileoffset of prev instance file for this entry, or 0 */
440d4afb5ceSopenharmony_ci		bp += wq32(&buf[bp], temp);
441d4afb5ceSopenharmony_ci		bp += wq32(&buf[bp], tif->file_index);
442d4afb5ceSopenharmony_ci		bp += wq32(&buf[bp], tif->total);
443d4afb5ceSopenharmony_ci
444d4afb5ceSopenharmony_ci		/* remove any pointers into this disposable lac footprint */
445d4afb5ceSopenharmony_ci		tif->owner->inst_file_list = NULL;
446d4afb5ceSopenharmony_ci
447d4afb5ceSopenharmony_ci		memcpy(&buf[bp], &tif->vli, (size_t)tif->count);
448d4afb5ceSopenharmony_ci		bp += tif->count;
449d4afb5ceSopenharmony_ci
450d4afb5ceSopenharmony_ci		i = tif->lines_list;
451d4afb5ceSopenharmony_ci		while (i) {
452d4afb5ceSopenharmony_ci			spill(i->count, 0);
453d4afb5ceSopenharmony_ci			memcpy(&buf[bp], &i->vli, (size_t)i->count);
454d4afb5ceSopenharmony_ci			bp += i->count;
455d4afb5ceSopenharmony_ci
456d4afb5ceSopenharmony_ci			i = i->lines_next;
457d4afb5ceSopenharmony_ci		}
458d4afb5ceSopenharmony_ci
459d4afb5ceSopenharmony_ci		tif = tif->inst_file_next;
460d4afb5ceSopenharmony_ci	}
461d4afb5ceSopenharmony_ci
462d4afb5ceSopenharmony_ci	spill(0, 1);
463d4afb5ceSopenharmony_ci
464d4afb5ceSopenharmony_ci	assert(lseek(t->fd, 0, SEEK_END) == (off_t)t->c);
465d4afb5ceSopenharmony_ci
466d4afb5ceSopenharmony_ci	if (t->lwsac_input_head) {
467d4afb5ceSopenharmony_ci		lwsac_input_size = lwsac_total_alloc(t->lwsac_input_head);
468d4afb5ceSopenharmony_ci		if (lwsac_input_size > t->worst_lwsac_input_size)
469d4afb5ceSopenharmony_ci			t->worst_lwsac_input_size = lwsac_input_size;
470d4afb5ceSopenharmony_ci	}
471d4afb5ceSopenharmony_ci
472d4afb5ceSopenharmony_ci	/*
473d4afb5ceSopenharmony_ci	 * those per-file allocations are all on a separate lac so we can
474d4afb5ceSopenharmony_ci	 * free it cleanly afterwards
475d4afb5ceSopenharmony_ci	 */
476d4afb5ceSopenharmony_ci	lwsac_free(&t->lwsac_input_head);
477d4afb5ceSopenharmony_ci
478d4afb5ceSopenharmony_ci	/* and lose the pointer into the deallocated lac */
479d4afb5ceSopenharmony_ci	t->tif_list = NULL;
480d4afb5ceSopenharmony_ci
481d4afb5ceSopenharmony_ci	return 0;
482d4afb5ceSopenharmony_ci}
483d4afb5ceSopenharmony_ci
484d4afb5ceSopenharmony_ci/*
485d4afb5ceSopenharmony_ci * 0 = punctuation, whitespace, brackets etc
486d4afb5ceSopenharmony_ci * 1 = character inside symbol set
487d4afb5ceSopenharmony_ci * 2 = upper-case character inside symbol set
488d4afb5ceSopenharmony_ci */
489d4afb5ceSopenharmony_ci
490d4afb5ceSopenharmony_cistatic char classify[] = {
491d4afb5ceSopenharmony_ci	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
492d4afb5ceSopenharmony_ci	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
493d4afb5ceSopenharmony_ci	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
494d4afb5ceSopenharmony_ci	1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
495d4afb5ceSopenharmony_ci	0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
496d4afb5ceSopenharmony_ci	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 1, //1,
497d4afb5ceSopenharmony_ci	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
498d4afb5ceSopenharmony_ci	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
499d4afb5ceSopenharmony_ci	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
500d4afb5ceSopenharmony_ci	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
501d4afb5ceSopenharmony_ci	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
502d4afb5ceSopenharmony_ci	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
503d4afb5ceSopenharmony_ci	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
504d4afb5ceSopenharmony_ci	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
505d4afb5ceSopenharmony_ci	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
506d4afb5ceSopenharmony_ci	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
507d4afb5ceSopenharmony_ci};
508d4afb5ceSopenharmony_ci
509d4afb5ceSopenharmony_ci#if 0
510d4afb5ceSopenharmony_cistatic const char *
511d4afb5ceSopenharmony_ciname_entry(struct lws_fts_entry *e1, char *s, int len)
512d4afb5ceSopenharmony_ci{
513d4afb5ceSopenharmony_ci	struct lws_fts_entry *e2;
514d4afb5ceSopenharmony_ci	int n = len;
515d4afb5ceSopenharmony_ci
516d4afb5ceSopenharmony_ci	s[--n] = '\0';
517d4afb5ceSopenharmony_ci
518d4afb5ceSopenharmony_ci	e2 = e1;
519d4afb5ceSopenharmony_ci	while (e2) {
520d4afb5ceSopenharmony_ci		if (e2->suffix) {
521d4afb5ceSopenharmony_ci			if ((int)e2->suffix_len < n) {
522d4afb5ceSopenharmony_ci				n -= e2->suffix_len;
523d4afb5ceSopenharmony_ci				memcpy(&s[n], e2->suffix, e2->suffix_len);
524d4afb5ceSopenharmony_ci			}
525d4afb5ceSopenharmony_ci		} else {
526d4afb5ceSopenharmony_ci			n--;
527d4afb5ceSopenharmony_ci			s[n] = e2->c;
528d4afb5ceSopenharmony_ci		}
529d4afb5ceSopenharmony_ci
530d4afb5ceSopenharmony_ci		e2 = e2->parent;
531d4afb5ceSopenharmony_ci	}
532d4afb5ceSopenharmony_ci
533d4afb5ceSopenharmony_ci	return &s[n + 1];
534d4afb5ceSopenharmony_ci}
535d4afb5ceSopenharmony_ci#endif
536d4afb5ceSopenharmony_ci
537d4afb5ceSopenharmony_ci/*
538d4afb5ceSopenharmony_ci * as we parse the input, we create a line length table for the file index.
539d4afb5ceSopenharmony_ci * Only the file header has been written before we start doing this.
540d4afb5ceSopenharmony_ci */
541d4afb5ceSopenharmony_ci
542d4afb5ceSopenharmony_ciint
543d4afb5ceSopenharmony_cilws_fts_fill(struct lws_fts *t, uint32_t file_index, const char *buf,
544d4afb5ceSopenharmony_ci	     size_t len)
545d4afb5ceSopenharmony_ci{
546d4afb5ceSopenharmony_ci	unsigned long long tf = (unsigned long long)lws_now_usecs();
547d4afb5ceSopenharmony_ci	unsigned char c, linetable[256], vlibuf[8];
548d4afb5ceSopenharmony_ci	struct lws_fts_entry *e, *e1, *dcl;
549d4afb5ceSopenharmony_ci	struct lws_fts_instance_file *tif;
550d4afb5ceSopenharmony_ci	int bp = 0, sline, chars, m;
551d4afb5ceSopenharmony_ci	char *osuff, skipline = 0;
552d4afb5ceSopenharmony_ci	struct lws_fts_lines *tl;
553d4afb5ceSopenharmony_ci	unsigned int olen, n;
554d4afb5ceSopenharmony_ci	off_t lbh;
555d4afb5ceSopenharmony_ci
556d4afb5ceSopenharmony_ci	if ((int)file_index != t->last_file_index) {
557d4afb5ceSopenharmony_ci		if (t->last_file_index >= 0)
558d4afb5ceSopenharmony_ci			finalize_per_input(t);
559d4afb5ceSopenharmony_ci		t->last_file_index = (int)file_index;
560d4afb5ceSopenharmony_ci		t->line_number = 1;
561d4afb5ceSopenharmony_ci		t->chars_in_line = 0;
562d4afb5ceSopenharmony_ci		t->lines_in_unsealed_linetable = 0;
563d4afb5ceSopenharmony_ci	}
564d4afb5ceSopenharmony_ci
565d4afb5ceSopenharmony_ci	t->agg_raw_input += len;
566d4afb5ceSopenharmony_ci
567d4afb5ceSopenharmony_ciresume:
568d4afb5ceSopenharmony_ci
569d4afb5ceSopenharmony_ci	chars = 0;
570d4afb5ceSopenharmony_ci	lbh = (off_t)t->c;
571d4afb5ceSopenharmony_ci	sline = t->line_number;
572d4afb5ceSopenharmony_ci	bp += g16(&linetable[bp], 0);
573d4afb5ceSopenharmony_ci	bp += g16(&linetable[bp], 0);
574d4afb5ceSopenharmony_ci	bp += g32(&linetable[bp], 0);
575d4afb5ceSopenharmony_ci
576d4afb5ceSopenharmony_ci	while (len) {
577d4afb5ceSopenharmony_ci		char go_around = 0;
578d4afb5ceSopenharmony_ci
579d4afb5ceSopenharmony_ci		if (t->lines_in_unsealed_linetable >= LWS_FTS_LINES_PER_CHUNK)
580d4afb5ceSopenharmony_ci			break;
581d4afb5ceSopenharmony_ci
582d4afb5ceSopenharmony_ci		len--;
583d4afb5ceSopenharmony_ci
584d4afb5ceSopenharmony_ci		c = (unsigned char)*buf++;
585d4afb5ceSopenharmony_ci		t->chars_in_line++;
586d4afb5ceSopenharmony_ci		if (c == '\n') {
587d4afb5ceSopenharmony_ci			skipline = 0;
588d4afb5ceSopenharmony_ci			t->filepath_list->total_lines++;
589d4afb5ceSopenharmony_ci			t->lines_in_unsealed_linetable++;
590d4afb5ceSopenharmony_ci			t->line_number++;
591d4afb5ceSopenharmony_ci
592d4afb5ceSopenharmony_ci			bp += wq32(&linetable[bp], (uint32_t)t->chars_in_line);
593d4afb5ceSopenharmony_ci			if ((unsigned int)bp > sizeof(linetable) - 6) {
594d4afb5ceSopenharmony_ci				if ((int)write(t->fd, linetable, (unsigned int)bp) != bp) {
595d4afb5ceSopenharmony_ci					lwsl_err("%s: linetable write failed\n",
596d4afb5ceSopenharmony_ci							__func__);
597d4afb5ceSopenharmony_ci					return 1;
598d4afb5ceSopenharmony_ci				}
599d4afb5ceSopenharmony_ci				t->c += (unsigned int)bp;
600d4afb5ceSopenharmony_ci				bp = 0;
601d4afb5ceSopenharmony_ci				// assert(lseek(t->fd, 0, SEEK_END) == t->c);
602d4afb5ceSopenharmony_ci			}
603d4afb5ceSopenharmony_ci
604d4afb5ceSopenharmony_ci			chars += t->chars_in_line;
605d4afb5ceSopenharmony_ci			t->chars_in_line = 0;
606d4afb5ceSopenharmony_ci
607d4afb5ceSopenharmony_ci			/*
608d4afb5ceSopenharmony_ci			 * Detect overlength lines and skip them (eg, BASE64
609d4afb5ceSopenharmony_ci			 * in css etc)
610d4afb5ceSopenharmony_ci			 */
611d4afb5ceSopenharmony_ci
612d4afb5ceSopenharmony_ci			if (len > 200) {
613d4afb5ceSopenharmony_ci				n = 0;
614d4afb5ceSopenharmony_ci				m = 0;
615d4afb5ceSopenharmony_ci				while (n < 200 && m < 80 && buf[n] != '\n') {
616d4afb5ceSopenharmony_ci				       if (buf[n] == ' ' || buf[n] == '\t')
617d4afb5ceSopenharmony_ci					       m = 0;
618d4afb5ceSopenharmony_ci					n++;
619d4afb5ceSopenharmony_ci					m++;
620d4afb5ceSopenharmony_ci				}
621d4afb5ceSopenharmony_ci
622d4afb5ceSopenharmony_ci				/* 80 lines no whitespace, or >=200-char line */
623d4afb5ceSopenharmony_ci
624d4afb5ceSopenharmony_ci				if (m == 80 || n == 200)
625d4afb5ceSopenharmony_ci					skipline = 1;
626d4afb5ceSopenharmony_ci			}
627d4afb5ceSopenharmony_ci
628d4afb5ceSopenharmony_ci			goto seal;
629d4afb5ceSopenharmony_ci		}
630d4afb5ceSopenharmony_ci		if (skipline)
631d4afb5ceSopenharmony_ci			continue;
632d4afb5ceSopenharmony_ci
633d4afb5ceSopenharmony_ci		m = classify[(int)c];
634d4afb5ceSopenharmony_ci		if (!m)
635d4afb5ceSopenharmony_ci			goto seal;
636d4afb5ceSopenharmony_ci		if (m == 2)
637d4afb5ceSopenharmony_ci			c = (unsigned char)((char)c + 'a' - 'A');
638d4afb5ceSopenharmony_ci
639d4afb5ceSopenharmony_ci		if (t->aggregate) {
640d4afb5ceSopenharmony_ci
641d4afb5ceSopenharmony_ci			/*
642d4afb5ceSopenharmony_ci			 * We created a trie entry for an earlier char in this
643d4afb5ceSopenharmony_ci			 * symbol already.  So we know at the moment, any
644d4afb5ceSopenharmony_ci			 * further chars in the symbol are the only children.
645d4afb5ceSopenharmony_ci			 *
646d4afb5ceSopenharmony_ci			 * Aggregate them and add them as a string suffix to
647d4afb5ceSopenharmony_ci			 * the trie symbol at the end (when we know how much to
648d4afb5ceSopenharmony_ci			 * allocate).
649d4afb5ceSopenharmony_ci			 */
650d4afb5ceSopenharmony_ci
651d4afb5ceSopenharmony_ci			if (t->agg_pos < sizeof(t->agg) - 1)
652d4afb5ceSopenharmony_ci				/* symbol is not too long to stash */
653d4afb5ceSopenharmony_ci				t->agg[t->agg_pos++] = c;
654d4afb5ceSopenharmony_ci
655d4afb5ceSopenharmony_ci			continue;
656d4afb5ceSopenharmony_ci		}
657d4afb5ceSopenharmony_ci
658d4afb5ceSopenharmony_ci		if (t->str_match_pos) {
659d4afb5ceSopenharmony_ci			go_around = 1;
660d4afb5ceSopenharmony_ci			goto seal;
661d4afb5ceSopenharmony_ci		}
662d4afb5ceSopenharmony_ci
663d4afb5ceSopenharmony_ci		/* zeroth-iteration child matching */
664d4afb5ceSopenharmony_ci
665d4afb5ceSopenharmony_ci		if (t->parser == t->root) {
666d4afb5ceSopenharmony_ci			e = t->root_lookup[(int)c];
667d4afb5ceSopenharmony_ci			if (e) {
668d4afb5ceSopenharmony_ci				t->parser = e;
669d4afb5ceSopenharmony_ci				continue;
670d4afb5ceSopenharmony_ci			}
671d4afb5ceSopenharmony_ci		} else {
672d4afb5ceSopenharmony_ci
673d4afb5ceSopenharmony_ci			/* look for the char amongst the children */
674d4afb5ceSopenharmony_ci
675d4afb5ceSopenharmony_ci			e = t->parser->child_list;
676d4afb5ceSopenharmony_ci			while (e) {
677d4afb5ceSopenharmony_ci
678d4afb5ceSopenharmony_ci				/* since they're alpha ordered... */
679d4afb5ceSopenharmony_ci				if (e->c > c) {
680d4afb5ceSopenharmony_ci					e = NULL;
681d4afb5ceSopenharmony_ci					break;
682d4afb5ceSopenharmony_ci				}
683d4afb5ceSopenharmony_ci				if (e->c == c) {
684d4afb5ceSopenharmony_ci					t->parser = e;
685d4afb5ceSopenharmony_ci
686d4afb5ceSopenharmony_ci					if (e->suffix)
687d4afb5ceSopenharmony_ci						t->str_match_pos = 1;
688d4afb5ceSopenharmony_ci
689d4afb5ceSopenharmony_ci					break;
690d4afb5ceSopenharmony_ci				}
691d4afb5ceSopenharmony_ci
692d4afb5ceSopenharmony_ci				e = e->sibling;
693d4afb5ceSopenharmony_ci			}
694d4afb5ceSopenharmony_ci
695d4afb5ceSopenharmony_ci			if (e)
696d4afb5ceSopenharmony_ci				continue;
697d4afb5ceSopenharmony_ci		}
698d4afb5ceSopenharmony_ci
699d4afb5ceSopenharmony_ci		/*
700d4afb5ceSopenharmony_ci		 * we are blazing a new trail, add a new child representing
701d4afb5ceSopenharmony_ci		 * the whole suffix that couldn't be matched until now.
702d4afb5ceSopenharmony_ci		 */
703d4afb5ceSopenharmony_ci
704d4afb5ceSopenharmony_ci		e = lws_fts_entry_child_add(t, c, t->parser);
705d4afb5ceSopenharmony_ci		if (!e) {
706d4afb5ceSopenharmony_ci			lwsl_err("%s: lws_fts_entry_child_add failed\n",
707d4afb5ceSopenharmony_ci					__func__);
708d4afb5ceSopenharmony_ci			return 1;
709d4afb5ceSopenharmony_ci		}
710d4afb5ceSopenharmony_ci
711d4afb5ceSopenharmony_ci		/* if it's the root node, keep the root_lookup table in sync */
712d4afb5ceSopenharmony_ci
713d4afb5ceSopenharmony_ci		if (t->parser == t->root)
714d4afb5ceSopenharmony_ci			t->root_lookup[(int)c] = e;
715d4afb5ceSopenharmony_ci
716d4afb5ceSopenharmony_ci		/* follow the new path */
717d4afb5ceSopenharmony_ci		t->parser = e;
718d4afb5ceSopenharmony_ci
719d4afb5ceSopenharmony_ci		{
720d4afb5ceSopenharmony_ci			struct lws_fts_entry **pe = &e->child_list;
721d4afb5ceSopenharmony_ci			while (*pe) {
722d4afb5ceSopenharmony_ci				assert((*pe)->parent == e);
723d4afb5ceSopenharmony_ci
724d4afb5ceSopenharmony_ci				pe = &(*pe)->sibling;
725d4afb5ceSopenharmony_ci			}
726d4afb5ceSopenharmony_ci		}
727d4afb5ceSopenharmony_ci
728d4afb5ceSopenharmony_ci		/*
729d4afb5ceSopenharmony_ci		 * If there are any more symbol characters coming, just
730d4afb5ceSopenharmony_ci		 * create a suffix string on t->parser instead of what must
731d4afb5ceSopenharmony_ci		 * currently be single-child nodes, since we just created e
732d4afb5ceSopenharmony_ci		 * as a child with a single character due to no existing match
733d4afb5ceSopenharmony_ci		 * on that single character... so if no match on 'h' with this
734d4afb5ceSopenharmony_ci		 * guy's parent, we created e that matches on the single char
735d4afb5ceSopenharmony_ci		 * 'h'.  If the symbol continues ... 'a' 'p' 'p' 'y', then
736d4afb5ceSopenharmony_ci		 * instead of creating singleton child nodes under e,
737d4afb5ceSopenharmony_ci		 * modify e to match on the whole string suffix "happy".
738d4afb5ceSopenharmony_ci		 *
739d4afb5ceSopenharmony_ci		 * If later "hoppy" appears, we will remove the suffix on e,
740d4afb5ceSopenharmony_ci		 * so it reverts to a char match for 'h', add singleton children
741d4afb5ceSopenharmony_ci		 * for 'a' and 'o', and attach a "ppy" suffix child to each of
742d4afb5ceSopenharmony_ci		 * those.
743d4afb5ceSopenharmony_ci		 *
744d4afb5ceSopenharmony_ci		 * We want to do this so we don't have to allocate trie entries
745d4afb5ceSopenharmony_ci		 * for every char in the string to save memory and consequently
746d4afb5ceSopenharmony_ci		 * time.
747d4afb5ceSopenharmony_ci		 *
748d4afb5ceSopenharmony_ci		 * Don't try this optimization if the parent is the root node...
749d4afb5ceSopenharmony_ci		 * it's not compatible with it's root_lookup table and it's
750d4afb5ceSopenharmony_ci		 * highly likely children off the root entry are going to have
751d4afb5ceSopenharmony_ci		 * to be fragmented.
752d4afb5ceSopenharmony_ci		 */
753d4afb5ceSopenharmony_ci
754d4afb5ceSopenharmony_ci		if (e->parent != t->root) {
755d4afb5ceSopenharmony_ci			t->aggregate = 1;
756d4afb5ceSopenharmony_ci			t->agg_pos = 0;
757d4afb5ceSopenharmony_ci		}
758d4afb5ceSopenharmony_ci
759d4afb5ceSopenharmony_ci		continue;
760d4afb5ceSopenharmony_ci
761d4afb5ceSopenharmony_ciseal:
762d4afb5ceSopenharmony_ci		if (t->str_match_pos) {
763d4afb5ceSopenharmony_ci
764d4afb5ceSopenharmony_ci			/*
765d4afb5ceSopenharmony_ci			 * We're partway through matching an elaborated string
766d4afb5ceSopenharmony_ci			 * on a child, not just a character.  String matches
767d4afb5ceSopenharmony_ci			 * only exist when we met a child entry that only had
768d4afb5ceSopenharmony_ci			 * one path until now... so we had an 'h', and the
769d4afb5ceSopenharmony_ci			 * only child had a string "hello".
770d4afb5ceSopenharmony_ci			 *
771d4afb5ceSopenharmony_ci			 * We are following the right path and will not need
772d4afb5ceSopenharmony_ci			 * to back up, but we may find as we go we have the
773d4afb5ceSopenharmony_ci			 * first instance of a second child path, eg, "help".
774d4afb5ceSopenharmony_ci			 *
775d4afb5ceSopenharmony_ci			 * When we get to the 'p', we have to split what was
776d4afb5ceSopenharmony_ci			 * the only string option "hello" into "hel" and then
777d4afb5ceSopenharmony_ci			 * two child entries, for "lo" and 'p'.
778d4afb5ceSopenharmony_ci			 */
779d4afb5ceSopenharmony_ci
780d4afb5ceSopenharmony_ci			if (c == t->parser->suffix[t->str_match_pos++]) {
781d4afb5ceSopenharmony_ci				if (t->str_match_pos < t->parser->suffix_len)
782d4afb5ceSopenharmony_ci					continue;
783d4afb5ceSopenharmony_ci
784d4afb5ceSopenharmony_ci				/*
785d4afb5ceSopenharmony_ci				 * We simply matched everything, continue
786d4afb5ceSopenharmony_ci				 * parsing normally from this trie entry.
787d4afb5ceSopenharmony_ci				 */
788d4afb5ceSopenharmony_ci
789d4afb5ceSopenharmony_ci				t->str_match_pos = 0;
790d4afb5ceSopenharmony_ci				continue;
791d4afb5ceSopenharmony_ci			}
792d4afb5ceSopenharmony_ci
793d4afb5ceSopenharmony_ci			/*
794d4afb5ceSopenharmony_ci			 * So... we hit a mismatch somewhere... it means we
795d4afb5ceSopenharmony_ci			 * have to split this string entry.
796d4afb5ceSopenharmony_ci			 *
797d4afb5ceSopenharmony_ci			 * We know the first char actually matched in order to
798d4afb5ceSopenharmony_ci			 * start down this road.  So for the current trie entry,
799d4afb5ceSopenharmony_ci			 * we need to truncate his suffix at the char before
800d4afb5ceSopenharmony_ci			 * this mismatched one, where we diverged (if the
801d4afb5ceSopenharmony_ci			 * second char, simply remove the suffix string from the
802d4afb5ceSopenharmony_ci			 * current trie entry to turn it back to a 1-char match)
803d4afb5ceSopenharmony_ci			 *
804d4afb5ceSopenharmony_ci			 * The original entry, which becomes the lhs post-split,
805d4afb5ceSopenharmony_ci			 * is t->parser.
806d4afb5ceSopenharmony_ci			 */
807d4afb5ceSopenharmony_ci
808d4afb5ceSopenharmony_ci			olen = t->parser->suffix_len;
809d4afb5ceSopenharmony_ci			osuff = t->parser->suffix;
810d4afb5ceSopenharmony_ci
811d4afb5ceSopenharmony_ci			if (t->str_match_pos == 2)
812d4afb5ceSopenharmony_ci				t->parser->suffix = NULL;
813d4afb5ceSopenharmony_ci			else
814d4afb5ceSopenharmony_ci				t->parser->suffix_len = t->str_match_pos - 1;
815d4afb5ceSopenharmony_ci
816d4afb5ceSopenharmony_ci			/*
817d4afb5ceSopenharmony_ci			 * Then we need to create a new child trie entry that
818d4afb5ceSopenharmony_ci			 * represents the remainder of the original string
819d4afb5ceSopenharmony_ci			 * path that we didn't match.  For the "hello" /
820d4afb5ceSopenharmony_ci			 * "help" case, this guy will have "lo".
821d4afb5ceSopenharmony_ci			 *
822d4afb5ceSopenharmony_ci			 * Any instances or children (not siblings...) that were
823d4afb5ceSopenharmony_ci			 * attached to the original trie entry must be detached
824d4afb5ceSopenharmony_ci			 * first and then migrate to this new guy that completes
825d4afb5ceSopenharmony_ci			 * the original string.
826d4afb5ceSopenharmony_ci			 */
827d4afb5ceSopenharmony_ci
828d4afb5ceSopenharmony_ci			dcl = t->parser->child_list;
829d4afb5ceSopenharmony_ci			m = (int)t->parser->child_count;
830d4afb5ceSopenharmony_ci
831d4afb5ceSopenharmony_ci			t->parser->child_list = NULL;
832d4afb5ceSopenharmony_ci			t->parser->child_count = 0;
833d4afb5ceSopenharmony_ci
834d4afb5ceSopenharmony_ci			e = lws_fts_entry_child_add(t, (unsigned char)
835d4afb5ceSopenharmony_ci					osuff[t->str_match_pos - 1], t->parser);
836d4afb5ceSopenharmony_ci			if (!e) {
837d4afb5ceSopenharmony_ci				lwsl_err("%s: lws_fts_entry_child_add fail1\n",
838d4afb5ceSopenharmony_ci						__func__);
839d4afb5ceSopenharmony_ci				return 1;
840d4afb5ceSopenharmony_ci			}
841d4afb5ceSopenharmony_ci
842d4afb5ceSopenharmony_ci			e->child_list = dcl;
843d4afb5ceSopenharmony_ci			e->child_count = (uint32_t)m;
844d4afb5ceSopenharmony_ci			/*
845d4afb5ceSopenharmony_ci			 * any children we took over must point to us as the
846d4afb5ceSopenharmony_ci			 * parent now they appear on our child list
847d4afb5ceSopenharmony_ci			 */
848d4afb5ceSopenharmony_ci			e1 = e->child_list;
849d4afb5ceSopenharmony_ci			while (e1) {
850d4afb5ceSopenharmony_ci				e1->parent = e;
851d4afb5ceSopenharmony_ci				e1 = e1->sibling;
852d4afb5ceSopenharmony_ci			}
853d4afb5ceSopenharmony_ci
854d4afb5ceSopenharmony_ci			/*
855d4afb5ceSopenharmony_ci			 * We detached any children, gave them to the new guy
856d4afb5ceSopenharmony_ci			 * and replaced them with just our new guy
857d4afb5ceSopenharmony_ci			 */
858d4afb5ceSopenharmony_ci			t->parser->child_count = 1;
859d4afb5ceSopenharmony_ci			t->parser->child_list = e;
860d4afb5ceSopenharmony_ci
861d4afb5ceSopenharmony_ci			/*
862d4afb5ceSopenharmony_ci			 * any instances that belonged to the original entry we
863d4afb5ceSopenharmony_ci			 * are splitting now must be reassigned to the end
864d4afb5ceSopenharmony_ci			 * part
865d4afb5ceSopenharmony_ci			 */
866d4afb5ceSopenharmony_ci
867d4afb5ceSopenharmony_ci			e->inst_file_list = t->parser->inst_file_list;
868d4afb5ceSopenharmony_ci			if (e->inst_file_list)
869d4afb5ceSopenharmony_ci				e->inst_file_list->owner = e;
870d4afb5ceSopenharmony_ci			t->parser->inst_file_list = NULL;
871d4afb5ceSopenharmony_ci			e->instance_count = t->parser->instance_count;
872d4afb5ceSopenharmony_ci			t->parser->instance_count = 0;
873d4afb5ceSopenharmony_ci
874d4afb5ceSopenharmony_ci			e->ofs_last_inst_file = t->parser->ofs_last_inst_file;
875d4afb5ceSopenharmony_ci			t->parser->ofs_last_inst_file = 0;
876d4afb5ceSopenharmony_ci
877d4afb5ceSopenharmony_ci			if (t->str_match_pos != olen) {
878d4afb5ceSopenharmony_ci				/* we diverged partway */
879d4afb5ceSopenharmony_ci				e->suffix = &osuff[t->str_match_pos - 1];
880d4afb5ceSopenharmony_ci				e->suffix_len = olen - (t->str_match_pos - 1);
881d4afb5ceSopenharmony_ci			}
882d4afb5ceSopenharmony_ci
883d4afb5ceSopenharmony_ci			/*
884d4afb5ceSopenharmony_ci			 * if the current char is a terminal, skip creating a
885d4afb5ceSopenharmony_ci			 * new way forward.
886d4afb5ceSopenharmony_ci			 */
887d4afb5ceSopenharmony_ci
888d4afb5ceSopenharmony_ci			if (classify[(int)c]) {
889d4afb5ceSopenharmony_ci
890d4afb5ceSopenharmony_ci				/*
891d4afb5ceSopenharmony_ci				 * Lastly we need to create a new child trie
892d4afb5ceSopenharmony_ci				 * entry that represents the new way forward
893d4afb5ceSopenharmony_ci				 * from the point that we diverged.  For the
894d4afb5ceSopenharmony_ci				 * "hello" / "help" case, this guy will start
895d4afb5ceSopenharmony_ci				 * as a child of "hel" with the single
896d4afb5ceSopenharmony_ci				 * character match 'p'.
897d4afb5ceSopenharmony_ci				 *
898d4afb5ceSopenharmony_ci				 * Since he becomes the current parser context,
899d4afb5ceSopenharmony_ci				 * more symbol characters may be coming to make
900d4afb5ceSopenharmony_ci				 * him into, eg, "helping", in which case he
901d4afb5ceSopenharmony_ci				 * will acquire a suffix eventually of "ping"
902d4afb5ceSopenharmony_ci				 * via the aggregation stuff
903d4afb5ceSopenharmony_ci				 */
904d4afb5ceSopenharmony_ci
905d4afb5ceSopenharmony_ci				e = lws_fts_entry_child_add(t, c, t->parser);
906d4afb5ceSopenharmony_ci				if (!e) {
907d4afb5ceSopenharmony_ci					lwsl_err("%s: child_add fail2\n",
908d4afb5ceSopenharmony_ci						 __func__);
909d4afb5ceSopenharmony_ci					return 1;
910d4afb5ceSopenharmony_ci				}
911d4afb5ceSopenharmony_ci			}
912d4afb5ceSopenharmony_ci
913d4afb5ceSopenharmony_ci			/* go on following this path */
914d4afb5ceSopenharmony_ci			t->parser = e;
915d4afb5ceSopenharmony_ci
916d4afb5ceSopenharmony_ci			t->aggregate = 1;
917d4afb5ceSopenharmony_ci			t->agg_pos = 0;
918d4afb5ceSopenharmony_ci
919d4afb5ceSopenharmony_ci			t->str_match_pos = 0;
920d4afb5ceSopenharmony_ci
921d4afb5ceSopenharmony_ci			if (go_around)
922d4afb5ceSopenharmony_ci				continue;
923d4afb5ceSopenharmony_ci
924d4afb5ceSopenharmony_ci			/* this is intended to be a seal */
925d4afb5ceSopenharmony_ci		}
926d4afb5ceSopenharmony_ci
927d4afb5ceSopenharmony_ci
928d4afb5ceSopenharmony_ci		/* end of token */
929d4afb5ceSopenharmony_ci
930d4afb5ceSopenharmony_ci		if (t->aggregate && t->agg_pos) {
931d4afb5ceSopenharmony_ci
932d4afb5ceSopenharmony_ci			/* if nothing in agg[]: leave as single char match */
933d4afb5ceSopenharmony_ci
934d4afb5ceSopenharmony_ci			/* otherwise copy out the symbol aggregation */
935d4afb5ceSopenharmony_ci			t->parser->suffix = lwsac_use(&t->lwsac_head,
936d4afb5ceSopenharmony_ci						    t->agg_pos + 1,
937d4afb5ceSopenharmony_ci						    TRIE_LWSAC_BLOCK_SIZE);
938d4afb5ceSopenharmony_ci			if (!t->parser->suffix) {
939d4afb5ceSopenharmony_ci				lwsl_err("%s: lac for suffix failed\n",
940d4afb5ceSopenharmony_ci						__func__);
941d4afb5ceSopenharmony_ci				return 1;
942d4afb5ceSopenharmony_ci			}
943d4afb5ceSopenharmony_ci
944d4afb5ceSopenharmony_ci			/* add the first char at the beginning */
945d4afb5ceSopenharmony_ci			*t->parser->suffix = (char)t->parser->c;
946d4afb5ceSopenharmony_ci			/* and then add the agg buffer stuff */
947d4afb5ceSopenharmony_ci			memcpy(t->parser->suffix + 1, t->agg, t->agg_pos);
948d4afb5ceSopenharmony_ci			t->parser->suffix_len = t->agg_pos + 1;
949d4afb5ceSopenharmony_ci		}
950d4afb5ceSopenharmony_ci		t->aggregate = 0;
951d4afb5ceSopenharmony_ci
952d4afb5ceSopenharmony_ci		if (t->parser == t->root) /* multiple terminal chars */
953d4afb5ceSopenharmony_ci			continue;
954d4afb5ceSopenharmony_ci
955d4afb5ceSopenharmony_ci		if (!t->parser->inst_file_list ||
956d4afb5ceSopenharmony_ci		    t->parser->inst_file_list->file_index != file_index) {
957d4afb5ceSopenharmony_ci			tif = lwsac_use(&t->lwsac_input_head, sizeof(*tif),
958d4afb5ceSopenharmony_ci				      TRIE_LWSAC_BLOCK_SIZE);
959d4afb5ceSopenharmony_ci			if (!tif) {
960d4afb5ceSopenharmony_ci				lwsl_err("%s: lac for tif failed\n",
961d4afb5ceSopenharmony_ci						__func__);
962d4afb5ceSopenharmony_ci				return 1;
963d4afb5ceSopenharmony_ci			}
964d4afb5ceSopenharmony_ci
965d4afb5ceSopenharmony_ci			tif->file_index = file_index;
966d4afb5ceSopenharmony_ci			tif->owner = t->parser;
967d4afb5ceSopenharmony_ci			tif->lines_list = NULL;
968d4afb5ceSopenharmony_ci			tif->lines_tail = NULL;
969d4afb5ceSopenharmony_ci			tif->total = 0;
970d4afb5ceSopenharmony_ci			tif->count = 0;
971d4afb5ceSopenharmony_ci			tif->inst_file_next = t->tif_list;
972d4afb5ceSopenharmony_ci			t->tif_list = tif;
973d4afb5ceSopenharmony_ci
974d4afb5ceSopenharmony_ci			t->parser->inst_file_list = tif;
975d4afb5ceSopenharmony_ci		}
976d4afb5ceSopenharmony_ci
977d4afb5ceSopenharmony_ci		/*
978d4afb5ceSopenharmony_ci		 * A naive allocation strategy for this leads to 50% of the
979d4afb5ceSopenharmony_ci		 * total inmem lac allocation being for line numbers...
980d4afb5ceSopenharmony_ci		 *
981d4afb5ceSopenharmony_ci		 * It's mainly solved by only holding the instance and line
982d4afb5ceSopenharmony_ci		 * number tables for the duration of a file being input, as soon
983d4afb5ceSopenharmony_ci		 * as one input file is finished it is written to disk.
984d4afb5ceSopenharmony_ci		 *
985d4afb5ceSopenharmony_ci		 * For the common case of 1 - ~3 matches the line number are
986d4afb5ceSopenharmony_ci		 * stored in a small VLI array inside the filepath inst.  If the
987d4afb5ceSopenharmony_ci		 * next one won't fit, it allocates a line number struct with
988d4afb5ceSopenharmony_ci		 * more vli space and continues chaining those if needed.
989d4afb5ceSopenharmony_ci		 */
990d4afb5ceSopenharmony_ci
991d4afb5ceSopenharmony_ci		n = (unsigned int)wq32(vlibuf, (uint32_t)t->line_number);
992d4afb5ceSopenharmony_ci		tif = t->parser->inst_file_list;
993d4afb5ceSopenharmony_ci
994d4afb5ceSopenharmony_ci		if (!tif->lines_list) {
995d4afb5ceSopenharmony_ci			/* we are still trying to use the file inst vli */
996d4afb5ceSopenharmony_ci			if (LWS_ARRAY_SIZE(tif->vli) - (size_t)tif->count >= n) {
997d4afb5ceSopenharmony_ci				tif->count = (char)((char)tif->count + (char)wq32(tif->vli + tif->count,
998d4afb5ceSopenharmony_ci						   (uint32_t)t->line_number));
999d4afb5ceSopenharmony_ci				goto after;
1000d4afb5ceSopenharmony_ci			}
1001d4afb5ceSopenharmony_ci			/* we are going to have to allocate */
1002d4afb5ceSopenharmony_ci		}
1003d4afb5ceSopenharmony_ci
1004d4afb5ceSopenharmony_ci		/* can we add to an existing line numbers struct? */
1005d4afb5ceSopenharmony_ci		if (tif->lines_tail &&
1006d4afb5ceSopenharmony_ci		    LWS_ARRAY_SIZE(tif->lines_tail->vli) -
1007d4afb5ceSopenharmony_ci				(unsigned char)tif->lines_tail->count >= n) {
1008d4afb5ceSopenharmony_ci			tif->lines_tail->count = (char)((char)tif->lines_tail->count + (char)wq32(tif->lines_tail->vli +
1009d4afb5ceSopenharmony_ci						       tif->lines_tail->count,
1010d4afb5ceSopenharmony_ci						       (uint32_t)t->line_number));
1011d4afb5ceSopenharmony_ci			goto after;
1012d4afb5ceSopenharmony_ci		}
1013d4afb5ceSopenharmony_ci
1014d4afb5ceSopenharmony_ci		/* either no existing line numbers struct at tail, or full */
1015d4afb5ceSopenharmony_ci
1016d4afb5ceSopenharmony_ci		/* have to create a(nother) line numbers struct */
1017d4afb5ceSopenharmony_ci		tl = lwsac_use(&t->lwsac_input_head, sizeof(*tl),
1018d4afb5ceSopenharmony_ci			     TRIE_LWSAC_BLOCK_SIZE);
1019d4afb5ceSopenharmony_ci		if (!tl) {
1020d4afb5ceSopenharmony_ci			lwsl_err("%s: lac for tl failed\n", __func__);
1021d4afb5ceSopenharmony_ci			return 1;
1022d4afb5ceSopenharmony_ci		}
1023d4afb5ceSopenharmony_ci		tl->lines_next = NULL;
1024d4afb5ceSopenharmony_ci		if (tif->lines_tail)
1025d4afb5ceSopenharmony_ci			tif->lines_tail->lines_next = tl;
1026d4afb5ceSopenharmony_ci
1027d4afb5ceSopenharmony_ci		tif->lines_tail = tl;
1028d4afb5ceSopenharmony_ci		if (!tif->lines_list)
1029d4afb5ceSopenharmony_ci			tif->lines_list = tl;
1030d4afb5ceSopenharmony_ci
1031d4afb5ceSopenharmony_ci		tl->count = (char)wq32(tl->vli, (uint32_t)t->line_number);
1032d4afb5ceSopenharmony_ciafter:
1033d4afb5ceSopenharmony_ci		tif->total++;
1034d4afb5ceSopenharmony_ci#if 0
1035d4afb5ceSopenharmony_ci		{
1036d4afb5ceSopenharmony_ci			char s[128];
1037d4afb5ceSopenharmony_ci			const char *ne = name_entry(t->parser, s, sizeof(s));
1038d4afb5ceSopenharmony_ci
1039d4afb5ceSopenharmony_ci			if (!strcmp(ne, "describ")) {
1040d4afb5ceSopenharmony_ci				lwsl_err("     %s %d\n", ne, t->str_match_pos);
1041d4afb5ceSopenharmony_ci				write(1, buf - 10, 20);
1042d4afb5ceSopenharmony_ci			}
1043d4afb5ceSopenharmony_ci		}
1044d4afb5ceSopenharmony_ci#endif
1045d4afb5ceSopenharmony_ci		t->parser->instance_count++;
1046d4afb5ceSopenharmony_ci		t->parser = t->root;
1047d4afb5ceSopenharmony_ci		t->str_match_pos = 0;
1048d4afb5ceSopenharmony_ci	}
1049d4afb5ceSopenharmony_ci
1050d4afb5ceSopenharmony_ci	/* seal off the line length table block */
1051d4afb5ceSopenharmony_ci
1052d4afb5ceSopenharmony_ci	if (bp) {
1053d4afb5ceSopenharmony_ci		if ((int)write(t->fd, linetable, (size_t)bp) != bp)
1054d4afb5ceSopenharmony_ci			return 1;
1055d4afb5ceSopenharmony_ci		t->c += (unsigned int)bp;
1056d4afb5ceSopenharmony_ci		bp = 0;
1057d4afb5ceSopenharmony_ci	}
1058d4afb5ceSopenharmony_ci
1059d4afb5ceSopenharmony_ci	if (lseek(t->fd, lbh, SEEK_SET) < 0) {
1060d4afb5ceSopenharmony_ci		lwsl_err("%s: seek to 0x%llx failed\n", __func__,
1061d4afb5ceSopenharmony_ci				(unsigned long long)lbh);
1062d4afb5ceSopenharmony_ci		return 1;
1063d4afb5ceSopenharmony_ci	}
1064d4afb5ceSopenharmony_ci
1065d4afb5ceSopenharmony_ci	g16(linetable, (uint16_t)(t->c - (jg2_file_offset)lbh));
1066d4afb5ceSopenharmony_ci	g16(linetable + 2, (uint16_t)(t->line_number - sline));
1067d4afb5ceSopenharmony_ci	g32(linetable + 4, (uint32_t)chars);
1068d4afb5ceSopenharmony_ci	if ((int)write(t->fd, linetable, 8) != 8) {
1069d4afb5ceSopenharmony_ci		lwsl_err("%s: write linetable header failed\n", __func__);
1070d4afb5ceSopenharmony_ci		return 1;
1071d4afb5ceSopenharmony_ci	}
1072d4afb5ceSopenharmony_ci
1073d4afb5ceSopenharmony_ci	assert(lseek(t->fd, 0, SEEK_END) == (off_t)t->c);
1074d4afb5ceSopenharmony_ci
1075d4afb5ceSopenharmony_ci	if (lseek(t->fd, (off_t)t->c, SEEK_SET) < 0) {
1076d4afb5ceSopenharmony_ci		lwsl_err("%s: end seek failed\n", __func__);
1077d4afb5ceSopenharmony_ci		return 1;
1078d4afb5ceSopenharmony_ci	}
1079d4afb5ceSopenharmony_ci
1080d4afb5ceSopenharmony_ci	bp = 0;
1081d4afb5ceSopenharmony_ci
1082d4afb5ceSopenharmony_ci	if (len) {
1083d4afb5ceSopenharmony_ci		t->lines_in_unsealed_linetable = 0;
1084d4afb5ceSopenharmony_ci		goto resume;
1085d4afb5ceSopenharmony_ci	}
1086d4afb5ceSopenharmony_ci
1087d4afb5ceSopenharmony_ci	/* dump the collected per-input instance and line data, and free it */
1088d4afb5ceSopenharmony_ci
1089d4afb5ceSopenharmony_ci	t->agg_trie_creation_us += (uint64_t)((uint64_t)lws_now_usecs() - tf);
1090d4afb5ceSopenharmony_ci
1091d4afb5ceSopenharmony_ci	return 0;
1092d4afb5ceSopenharmony_ci}
1093d4afb5ceSopenharmony_ci
1094d4afb5ceSopenharmony_ci/* refer to ./README.md */
1095d4afb5ceSopenharmony_ci
1096d4afb5ceSopenharmony_ciint
1097d4afb5ceSopenharmony_cilws_fts_serialize(struct lws_fts *t)
1098d4afb5ceSopenharmony_ci{
1099d4afb5ceSopenharmony_ci	struct lws_fts_filepath *fp = t->filepath_list, *ofp;
1100d4afb5ceSopenharmony_ci	unsigned long long tf = (unsigned long long)lws_now_usecs();
1101d4afb5ceSopenharmony_ci	struct lws_fts_entry *e, *e1, *s[256];
1102d4afb5ceSopenharmony_ci	unsigned char buf[8192], stasis;
1103d4afb5ceSopenharmony_ci	int n, bp, sp = 0, do_parent;
1104d4afb5ceSopenharmony_ci
1105d4afb5ceSopenharmony_ci	(void)tf;
1106d4afb5ceSopenharmony_ci	finalize_per_input(t);
1107d4afb5ceSopenharmony_ci
1108d4afb5ceSopenharmony_ci	/*
1109d4afb5ceSopenharmony_ci	 * Compute aggregated instance counts (parents should know the total
1110d4afb5ceSopenharmony_ci	 * number of instances below each child path)
1111d4afb5ceSopenharmony_ci	 *
1112d4afb5ceSopenharmony_ci	 *
1113d4afb5ceSopenharmony_ci	 * If we have
1114d4afb5ceSopenharmony_ci	 *
1115d4afb5ceSopenharmony_ci	 * (root) -> (c1) -> (c2)
1116d4afb5ceSopenharmony_ci	 *        -> (c3)
1117d4afb5ceSopenharmony_ci	 *
1118d4afb5ceSopenharmony_ci	 * we need to visit the nodes in the order
1119d4afb5ceSopenharmony_ci	 *
1120d4afb5ceSopenharmony_ci	 * c2, c1, c3, root
1121d4afb5ceSopenharmony_ci	 */
1122d4afb5ceSopenharmony_ci
1123d4afb5ceSopenharmony_ci	sp = 0;
1124d4afb5ceSopenharmony_ci	s[0] = t->root;
1125d4afb5ceSopenharmony_ci	do_parent = 0;
1126d4afb5ceSopenharmony_ci	while (sp >= 0) {
1127d4afb5ceSopenharmony_ci		int n;
1128d4afb5ceSopenharmony_ci
1129d4afb5ceSopenharmony_ci		/* aggregate in every antecedent */
1130d4afb5ceSopenharmony_ci
1131d4afb5ceSopenharmony_ci		for (n = 0; n <= sp; n++) {
1132d4afb5ceSopenharmony_ci			s[n]->agg_inst_count += s[sp]->instance_count;
1133d4afb5ceSopenharmony_ci			s[n]->agg_child_count += s[sp]->child_count;
1134d4afb5ceSopenharmony_ci		}
1135d4afb5ceSopenharmony_ci
1136d4afb5ceSopenharmony_ci		/* handle any children before the parent */
1137d4afb5ceSopenharmony_ci
1138d4afb5ceSopenharmony_ci		if (s[sp]->child_list) {
1139d4afb5ceSopenharmony_ci			if (sp + 1 == LWS_ARRAY_SIZE(s)) {
1140d4afb5ceSopenharmony_ci				lwsl_err("Stack too deep\n");
1141d4afb5ceSopenharmony_ci
1142d4afb5ceSopenharmony_ci				goto bail;
1143d4afb5ceSopenharmony_ci			}
1144d4afb5ceSopenharmony_ci
1145d4afb5ceSopenharmony_ci			s[sp + 1] = s[sp]->child_list;
1146d4afb5ceSopenharmony_ci			sp++;
1147d4afb5ceSopenharmony_ci			continue;
1148d4afb5ceSopenharmony_ci		}
1149d4afb5ceSopenharmony_ci
1150d4afb5ceSopenharmony_ci		do {
1151d4afb5ceSopenharmony_ci			if (s[sp]->sibling) {
1152d4afb5ceSopenharmony_ci				s[sp] = s[sp]->sibling;
1153d4afb5ceSopenharmony_ci				break;
1154d4afb5ceSopenharmony_ci			} else
1155d4afb5ceSopenharmony_ci				sp--;
1156d4afb5ceSopenharmony_ci		} while (sp >= 0);
1157d4afb5ceSopenharmony_ci	}
1158d4afb5ceSopenharmony_ci
1159d4afb5ceSopenharmony_ci	/* dump the filepaths and set prev */
1160d4afb5ceSopenharmony_ci
1161d4afb5ceSopenharmony_ci	fp = t->filepath_list;
1162d4afb5ceSopenharmony_ci	ofp = NULL;
1163d4afb5ceSopenharmony_ci	bp = 0;
1164d4afb5ceSopenharmony_ci	while (fp) {
1165d4afb5ceSopenharmony_ci
1166d4afb5ceSopenharmony_ci		fp->ofs = t->c + (unsigned int)bp;
1167d4afb5ceSopenharmony_ci		n = (int)strlen(fp->filepath);
1168d4afb5ceSopenharmony_ci		spill(15 + n, 0);
1169d4afb5ceSopenharmony_ci
1170d4afb5ceSopenharmony_ci		bp += wq32(&buf[bp], fp->line_table_ofs);
1171d4afb5ceSopenharmony_ci		bp += wq32(&buf[bp], (uint32_t)fp->total_lines);
1172d4afb5ceSopenharmony_ci		bp += wq32(&buf[bp], (uint32_t)n);
1173d4afb5ceSopenharmony_ci		memcpy(&buf[bp], fp->filepath, (unsigned int)n);
1174d4afb5ceSopenharmony_ci		bp += n;
1175d4afb5ceSopenharmony_ci
1176d4afb5ceSopenharmony_ci		fp->prev = ofp;
1177d4afb5ceSopenharmony_ci		ofp = fp;
1178d4afb5ceSopenharmony_ci		fp = fp->next;
1179d4afb5ceSopenharmony_ci	}
1180d4afb5ceSopenharmony_ci
1181d4afb5ceSopenharmony_ci	spill(0, 1);
1182d4afb5ceSopenharmony_ci
1183d4afb5ceSopenharmony_ci	/* record the fileoffset of the filepath map and filepath count */
1184d4afb5ceSopenharmony_ci
1185d4afb5ceSopenharmony_ci	if (lseek(t->fd, 0xc, SEEK_SET) < 0)
1186d4afb5ceSopenharmony_ci		goto bail_seek;
1187d4afb5ceSopenharmony_ci
1188d4afb5ceSopenharmony_ci	g32(buf, t->c + (unsigned int)bp);
1189d4afb5ceSopenharmony_ci	g32(buf + 4, (uint32_t)t->next_file_index);
1190d4afb5ceSopenharmony_ci	if ((int)write(t->fd, buf, 8) != 8)
1191d4afb5ceSopenharmony_ci		goto bail;
1192d4afb5ceSopenharmony_ci
1193d4afb5ceSopenharmony_ci	if (lseek(t->fd, (off_t)(t->c + (unsigned int)bp), SEEK_SET) < 0)
1194d4afb5ceSopenharmony_ci		goto bail_seek;
1195d4afb5ceSopenharmony_ci
1196d4afb5ceSopenharmony_ci	/* dump the filepath map, starting from index 0, which is at the tail */
1197d4afb5ceSopenharmony_ci
1198d4afb5ceSopenharmony_ci	fp = ofp;
1199d4afb5ceSopenharmony_ci	bp = 0;
1200d4afb5ceSopenharmony_ci	while (fp) {
1201d4afb5ceSopenharmony_ci		spill(5, 0);
1202d4afb5ceSopenharmony_ci		g32(buf + bp, fp->ofs);
1203d4afb5ceSopenharmony_ci		bp += 4;
1204d4afb5ceSopenharmony_ci		fp = fp->prev;
1205d4afb5ceSopenharmony_ci	}
1206d4afb5ceSopenharmony_ci	spill(0, 1);
1207d4afb5ceSopenharmony_ci
1208d4afb5ceSopenharmony_ci	/*
1209d4afb5ceSopenharmony_ci	 * The trie entries in reverse order... because of the reversal, we have
1210d4afb5ceSopenharmony_ci	 * always written children first, and marked them with their file offset
1211d4afb5ceSopenharmony_ci	 * before we come to refer to them.
1212d4afb5ceSopenharmony_ci	 */
1213d4afb5ceSopenharmony_ci
1214d4afb5ceSopenharmony_ci	bp = 0;
1215d4afb5ceSopenharmony_ci	sp = 0;
1216d4afb5ceSopenharmony_ci	s[0] = t->root;
1217d4afb5ceSopenharmony_ci	do_parent = 0;
1218d4afb5ceSopenharmony_ci	while (s[sp]) {
1219d4afb5ceSopenharmony_ci
1220d4afb5ceSopenharmony_ci		/* handle any children before the parent */
1221d4afb5ceSopenharmony_ci
1222d4afb5ceSopenharmony_ci		if (!do_parent && s[sp]->child_list) {
1223d4afb5ceSopenharmony_ci
1224d4afb5ceSopenharmony_ci			if (sp + 1 == LWS_ARRAY_SIZE(s)) {
1225d4afb5ceSopenharmony_ci				lwsl_err("Stack too deep\n");
1226d4afb5ceSopenharmony_ci
1227d4afb5ceSopenharmony_ci				goto bail;
1228d4afb5ceSopenharmony_ci			}
1229d4afb5ceSopenharmony_ci
1230d4afb5ceSopenharmony_ci			s[sp + 1] = s[sp]->child_list;
1231d4afb5ceSopenharmony_ci			sp++;
1232d4afb5ceSopenharmony_ci			continue;
1233d4afb5ceSopenharmony_ci		}
1234d4afb5ceSopenharmony_ci
1235d4afb5ceSopenharmony_ci		/* leaf nodes with no children */
1236d4afb5ceSopenharmony_ci
1237d4afb5ceSopenharmony_ci		e = s[sp];
1238d4afb5ceSopenharmony_ci		e->ofs = t->c + (unsigned int)bp;
1239d4afb5ceSopenharmony_ci
1240d4afb5ceSopenharmony_ci		/* write the trie entry header */
1241d4afb5ceSopenharmony_ci
1242d4afb5ceSopenharmony_ci		spill((3 * MAX_VLI), 0);
1243d4afb5ceSopenharmony_ci
1244d4afb5ceSopenharmony_ci		bp += wq32(&buf[bp], e->ofs_last_inst_file);
1245d4afb5ceSopenharmony_ci		bp += wq32(&buf[bp], e->child_count);
1246d4afb5ceSopenharmony_ci		bp += wq32(&buf[bp], e->instance_count);
1247d4afb5ceSopenharmony_ci		bp += wq32(&buf[bp], e->agg_inst_count);
1248d4afb5ceSopenharmony_ci
1249d4afb5ceSopenharmony_ci		/* sort the children in order of highest aggregate hits first */
1250d4afb5ceSopenharmony_ci
1251d4afb5ceSopenharmony_ci		do {
1252d4afb5ceSopenharmony_ci			struct lws_fts_entry **pe, *te1, *te2;
1253d4afb5ceSopenharmony_ci
1254d4afb5ceSopenharmony_ci			stasis = 1;
1255d4afb5ceSopenharmony_ci
1256d4afb5ceSopenharmony_ci			/* bubble sort keeps going until nothing changed */
1257d4afb5ceSopenharmony_ci
1258d4afb5ceSopenharmony_ci			pe = &e->child_list;
1259d4afb5ceSopenharmony_ci			while (*pe) {
1260d4afb5ceSopenharmony_ci
1261d4afb5ceSopenharmony_ci				te1 = *pe;
1262d4afb5ceSopenharmony_ci				te2 = te1->sibling;
1263d4afb5ceSopenharmony_ci
1264d4afb5ceSopenharmony_ci				if (te2 && te1->agg_inst_count <
1265d4afb5ceSopenharmony_ci					   te2->agg_inst_count) {
1266d4afb5ceSopenharmony_ci					stasis = 0;
1267d4afb5ceSopenharmony_ci
1268d4afb5ceSopenharmony_ci					*pe = te2;
1269d4afb5ceSopenharmony_ci					te1->sibling = te2->sibling;
1270d4afb5ceSopenharmony_ci					te2->sibling = te1;
1271d4afb5ceSopenharmony_ci				}
1272d4afb5ceSopenharmony_ci
1273d4afb5ceSopenharmony_ci				pe = &(*pe)->sibling;
1274d4afb5ceSopenharmony_ci			}
1275d4afb5ceSopenharmony_ci
1276d4afb5ceSopenharmony_ci		} while (!stasis);
1277d4afb5ceSopenharmony_ci
1278d4afb5ceSopenharmony_ci		/* write the children */
1279d4afb5ceSopenharmony_ci
1280d4afb5ceSopenharmony_ci		e1 = e->child_list;
1281d4afb5ceSopenharmony_ci		while (e1) {
1282d4afb5ceSopenharmony_ci			spill((5 * MAX_VLI) + e1->suffix_len + 1, 0);
1283d4afb5ceSopenharmony_ci
1284d4afb5ceSopenharmony_ci			bp += wq32(&buf[bp], e1->ofs);
1285d4afb5ceSopenharmony_ci			bp += wq32(&buf[bp], e1->instance_count);
1286d4afb5ceSopenharmony_ci			bp += wq32(&buf[bp], e1->agg_inst_count);
1287d4afb5ceSopenharmony_ci			bp += wq32(&buf[bp], e1->agg_child_count);
1288d4afb5ceSopenharmony_ci
1289d4afb5ceSopenharmony_ci			if (e1->suffix) { /* string  */
1290d4afb5ceSopenharmony_ci				bp += wq32(&buf[bp], e1->suffix_len);
1291d4afb5ceSopenharmony_ci				memmove(&buf[bp], e1->suffix, e1->suffix_len);
1292d4afb5ceSopenharmony_ci				bp += (int)e1->suffix_len;
1293d4afb5ceSopenharmony_ci			} else { /* char */
1294d4afb5ceSopenharmony_ci				bp += wq32(&buf[bp], 1);
1295d4afb5ceSopenharmony_ci				buf[bp++] = e1->c;
1296d4afb5ceSopenharmony_ci			}
1297d4afb5ceSopenharmony_ci#if 0
1298d4afb5ceSopenharmony_ci			if (e1->suffix && e1->suffix_len == 3 &&
1299d4afb5ceSopenharmony_ci			    !memcmp(e1->suffix, "cri", 3)) {
1300d4afb5ceSopenharmony_ci				struct lws_fts_entry *e2;
1301d4afb5ceSopenharmony_ci
1302d4afb5ceSopenharmony_ci				e2 = e1;
1303d4afb5ceSopenharmony_ci				while (e2){
1304d4afb5ceSopenharmony_ci					if (e2->suffix)
1305d4afb5ceSopenharmony_ci						lwsl_notice("%s\n", e2->suffix);
1306d4afb5ceSopenharmony_ci					else
1307d4afb5ceSopenharmony_ci						lwsl_notice("%c\n", e2->c);
1308d4afb5ceSopenharmony_ci
1309d4afb5ceSopenharmony_ci					e2 = e2->parent;
1310d4afb5ceSopenharmony_ci				}
1311d4afb5ceSopenharmony_ci
1312d4afb5ceSopenharmony_ci				lwsl_err("*** %c CRI inst %d ch %d\n", e1->parent->c,
1313d4afb5ceSopenharmony_ci						e1->instance_count, e1->child_count);
1314d4afb5ceSopenharmony_ci			}
1315d4afb5ceSopenharmony_ci#endif
1316d4afb5ceSopenharmony_ci			e1 = e1->sibling;
1317d4afb5ceSopenharmony_ci		}
1318d4afb5ceSopenharmony_ci
1319d4afb5ceSopenharmony_ci		/* if there are siblings, do those next */
1320d4afb5ceSopenharmony_ci
1321d4afb5ceSopenharmony_ci		if (do_parent) {
1322d4afb5ceSopenharmony_ci			do_parent = 0;
1323d4afb5ceSopenharmony_ci			sp--;
1324d4afb5ceSopenharmony_ci		}
1325d4afb5ceSopenharmony_ci
1326d4afb5ceSopenharmony_ci		if (s[sp]->sibling)
1327d4afb5ceSopenharmony_ci			s[sp] = s[sp]->sibling;
1328d4afb5ceSopenharmony_ci		else {
1329d4afb5ceSopenharmony_ci			/* if there are no siblings, do the parent */
1330d4afb5ceSopenharmony_ci			do_parent = 1;
1331d4afb5ceSopenharmony_ci			s[sp] = s[sp]->parent;
1332d4afb5ceSopenharmony_ci		}
1333d4afb5ceSopenharmony_ci	}
1334d4afb5ceSopenharmony_ci
1335d4afb5ceSopenharmony_ci	spill(0, 1);
1336d4afb5ceSopenharmony_ci
1337d4afb5ceSopenharmony_ci	assert(lseek(t->fd, 0, SEEK_END) == (off_t)t->c);
1338d4afb5ceSopenharmony_ci
1339d4afb5ceSopenharmony_ci	/* drop the correct root trie offset + file length into the header */
1340d4afb5ceSopenharmony_ci
1341d4afb5ceSopenharmony_ci	if (lseek(t->fd, 4, SEEK_SET) < 0) {
1342d4afb5ceSopenharmony_ci		lwsl_err("%s: unable to seek\n", __func__);
1343d4afb5ceSopenharmony_ci
1344d4afb5ceSopenharmony_ci		goto bail;
1345d4afb5ceSopenharmony_ci	}
1346d4afb5ceSopenharmony_ci
1347d4afb5ceSopenharmony_ci	g32(buf, t->root->ofs);
1348d4afb5ceSopenharmony_ci	g32(buf + 4, t->c);
1349d4afb5ceSopenharmony_ci	if (write(t->fd, buf, 0x8) != 0x8)
1350d4afb5ceSopenharmony_ci		goto bail;
1351d4afb5ceSopenharmony_ci
1352d4afb5ceSopenharmony_ci	lwsl_notice("%s: index %d files (%uMiB) cpu time %dms, "
1353d4afb5ceSopenharmony_ci		    "alloc: %dKiB + %dKiB, "
1354d4afb5ceSopenharmony_ci		    "serialize: %dms, file: %dKiB\n", __func__,
1355d4afb5ceSopenharmony_ci		    t->next_file_index,
1356d4afb5ceSopenharmony_ci		    (int)(t->agg_raw_input / (1024 * 1024)),
1357d4afb5ceSopenharmony_ci		    (int)(t->agg_trie_creation_us / 1000),
1358d4afb5ceSopenharmony_ci		    (int)(lwsac_total_alloc(t->lwsac_head) / 1024),
1359d4afb5ceSopenharmony_ci		    (int)(t->worst_lwsac_input_size / 1024),
1360d4afb5ceSopenharmony_ci		    (int)(((uint64_t)lws_now_usecs() - tf) / 1000),
1361d4afb5ceSopenharmony_ci		    (int)(t->c / 1024));
1362d4afb5ceSopenharmony_ci
1363d4afb5ceSopenharmony_ci	return 0;
1364d4afb5ceSopenharmony_ci
1365d4afb5ceSopenharmony_cibail_seek:
1366d4afb5ceSopenharmony_ci	lwsl_err("%s: problem seekings\n", __func__);
1367d4afb5ceSopenharmony_ci
1368d4afb5ceSopenharmony_cibail:
1369d4afb5ceSopenharmony_ci	return 1;
1370d4afb5ceSopenharmony_ci}
1371d4afb5ceSopenharmony_ci
1372d4afb5ceSopenharmony_ci
1373