1 /*
2    Copyright (C) 2009 Red Hat, Inc.
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 */
17 #include <config.h>
18 
19 #include <pthread.h>
20 #include <string.h>
21 #include <stdio.h>
22 
23 #include "glz-encoder.h"
24 #include "glz-encoder-dict.h"
25 #include "glz-encoder-priv.h"
26 
27 static void glz_enc_dictionary_reset(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr);
28 
29 /* turning all used images to free ones. If they are alive, calling the free_image callback for
30    each one */
__glz_dictionary_window_reset_images(SharedDictionary * dict)31 static inline void __glz_dictionary_window_reset_images(SharedDictionary *dict)
32 {
33     WindowImage *tmp;
34 
35     while (dict->window.used_images_head) {
36         tmp = dict->window.used_images_head;
37         dict->window.used_images_head = dict->window.used_images_head->next;
38         if (tmp->is_alive) {
39             dict->cur_usr->free_image(dict->cur_usr, tmp->usr_context);
40         }
41         tmp->next = dict->window.free_images;
42         tmp->is_alive = FALSE;
43         dict->window.free_images = tmp;
44     }
45     dict->window.used_images_tail = NULL;
46 }
47 
48 /* allocate window fields (no reset)*/
glz_dictionary_window_create(SharedDictionary * dict,uint32_t size)49 static bool glz_dictionary_window_create(SharedDictionary *dict, uint32_t size)
50 {
51     if (size > LZ_MAX_WINDOW_SIZE) {
52         return FALSE;
53     }
54 
55     dict->window.size_limit = size;
56     dict->window.segs = (WindowImageSegment *)(
57             dict->cur_usr->malloc(dict->cur_usr, sizeof(WindowImageSegment) * INIT_IMAGE_SEGS_NUM));
58 
59     if (!dict->window.segs) {
60         return FALSE;
61     }
62 
63     dict->window.segs_quota = INIT_IMAGE_SEGS_NUM;
64 
65     dict->window.encoders_heads = (uint32_t *)dict->cur_usr->malloc(dict->cur_usr,
66                                                             sizeof(uint32_t) * dict->max_encoders);
67 
68     if (!dict->window.encoders_heads) {
69         dict->cur_usr->free(dict->cur_usr, dict->window.segs);
70         return FALSE;
71     }
72 
73     dict->window.used_images_head = NULL;
74     dict->window.used_images_tail = NULL;
75     dict->window.free_images = NULL;
76     dict->window.pixels_so_far = 0;
77 
78     return TRUE;
79 }
80 
81 /* initializes an empty window (segs and encoder_heads should be pre allocated.
82    resets the image infos, and calls the free_image usr callback*/
glz_dictionary_window_reset(SharedDictionary * dict)83 static void glz_dictionary_window_reset(SharedDictionary *dict)
84 {
85     uint32_t i;
86     WindowImageSegment *seg, *last_seg;
87 
88     last_seg = dict->window.segs + dict->window.segs_quota;
89     /* reset free segs list */
90     dict->window.free_segs_head = 0;
91     for (seg = dict->window.segs, i = 0; seg < last_seg; seg++, i++) {
92         seg->next = i + 1;
93         seg->image = NULL;
94         seg->lines = NULL;
95         seg->lines_end = NULL;
96         seg->pixels_num = 0;
97         seg->pixels_so_far = 0;
98     }
99     dict->window.segs[dict->window.segs_quota - 1].next = NULL_IMAGE_SEG_ID;
100 
101     dict->window.used_segs_head = NULL_IMAGE_SEG_ID;
102     dict->window.used_segs_tail = NULL_IMAGE_SEG_ID;
103 
104     // reset encoders heads
105     for (i = 0; i < dict->max_encoders; i++) {
106         dict->window.encoders_heads[i] = NULL_IMAGE_SEG_ID;
107     }
108 
109     __glz_dictionary_window_reset_images(dict);
110 }
111 
glz_dictionary_reset_hash(SharedDictionary * dict)112 static inline void glz_dictionary_reset_hash(SharedDictionary *dict)
113 {
114     memset(dict->htab, 0, sizeof(HashEntry) * HASH_SIZE * HASH_CHAIN_SIZE);
115 #ifdef CHAINED_HASH
116     memset(dict->htab_counter, 0, HASH_SIZE * sizeof(uint8_t));
117 #endif
118 }
119 
glz_dictionary_window_destroy(SharedDictionary * dict)120 static inline void glz_dictionary_window_destroy(SharedDictionary *dict)
121 {
122     __glz_dictionary_window_reset_images(dict);
123 
124     if (dict->window.segs) {
125         dict->cur_usr->free(dict->cur_usr, dict->window.segs);
126         dict->window.segs = NULL;
127     }
128 
129     while (dict->window.free_images) {
130         WindowImage *tmp = dict->window.free_images;
131         dict->window.free_images = tmp->next;
132 
133         dict->cur_usr->free(dict->cur_usr, tmp);
134     }
135 
136     if (dict->window.encoders_heads) {
137         dict->cur_usr->free(dict->cur_usr, dict->window.encoders_heads);
138         dict->window.encoders_heads = NULL;
139     }
140 }
141 
142 /* logic removal only */
glz_dictionary_window_kill_image(SharedDictionary * dict,WindowImage * image)143 static inline void glz_dictionary_window_kill_image(SharedDictionary *dict, WindowImage *image)
144 {
145     image->is_alive = FALSE;
146 }
147 
glz_enc_dictionary_create(uint32_t size,uint32_t max_encoders,GlzEncoderUsrContext * usr)148 GlzEncDictContext *glz_enc_dictionary_create(uint32_t size, uint32_t max_encoders,
149                                              GlzEncoderUsrContext *usr)
150 {
151     SharedDictionary *dict;
152 
153     if (!(dict = (SharedDictionary *)usr->malloc(usr,
154                                                  sizeof(SharedDictionary)))) {
155         return NULL;
156     }
157 
158     dict->cur_usr = usr;
159     dict->last_image_id = 0;
160     dict->max_encoders = max_encoders;
161 
162     pthread_mutex_init(&dict->lock, NULL);
163     pthread_rwlock_init(&dict->rw_alloc_lock, NULL);
164 
165     dict->window.encoders_heads = NULL;
166 
167     // alloc window fields and reset
168     if (!glz_dictionary_window_create(dict, size)) {
169         dict->cur_usr->free(usr, dict);
170         return NULL;
171     }
172 
173     // reset window and hash
174     glz_enc_dictionary_reset((GlzEncDictContext *)dict, usr);
175 
176     return (GlzEncDictContext *)dict;
177 }
178 
glz_enc_dictionary_get_restore_data(GlzEncDictContext * opaque_dict,GlzEncDictRestoreData * out_data,GlzEncoderUsrContext * usr)179 void glz_enc_dictionary_get_restore_data(GlzEncDictContext *opaque_dict,
180                                          GlzEncDictRestoreData *out_data, GlzEncoderUsrContext *usr)
181 {
182     SharedDictionary *dict = (SharedDictionary *)opaque_dict;
183     dict->cur_usr = usr;
184     GLZ_ASSERT(dict->cur_usr, opaque_dict);
185     GLZ_ASSERT(dict->cur_usr, out_data);
186 
187     out_data->last_image_id = dict->last_image_id;
188     out_data->max_encoders = dict->max_encoders;
189     out_data->size = dict->window.size_limit;
190 }
191 
glz_enc_dictionary_restore(GlzEncDictRestoreData * restore_data,GlzEncoderUsrContext * usr)192 GlzEncDictContext *glz_enc_dictionary_restore(GlzEncDictRestoreData *restore_data,
193                                               GlzEncoderUsrContext *usr)
194 {
195     if (!restore_data) {
196         return NULL;
197     }
198     SharedDictionary *ret = (SharedDictionary *)glz_enc_dictionary_create(
199             restore_data->size, restore_data->max_encoders, usr);
200     if (!ret) {
201         return NULL;
202     }
203     ret->last_image_id = restore_data->last_image_id;
204     return ((GlzEncDictContext *)ret);
205 }
206 
207 /*  NOTE - you should use this routine only when no encoder uses the dictionary. */
glz_enc_dictionary_reset(GlzEncDictContext * opaque_dict,GlzEncoderUsrContext * usr)208 static void glz_enc_dictionary_reset(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr)
209 {
210     SharedDictionary *dict = (SharedDictionary *)opaque_dict;
211     dict->cur_usr = usr;
212     GLZ_ASSERT(dict->cur_usr, opaque_dict);
213 
214     dict->last_image_id = 0;
215     glz_dictionary_window_reset(dict);
216     glz_dictionary_reset_hash(dict);
217 }
218 
glz_enc_dictionary_destroy(GlzEncDictContext * opaque_dict,GlzEncoderUsrContext * usr)219 void glz_enc_dictionary_destroy(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr)
220 {
221     SharedDictionary *dict = (SharedDictionary *)opaque_dict;
222 
223     if (!opaque_dict) {
224         return;
225     }
226 
227     dict->cur_usr = usr;
228     glz_dictionary_window_destroy(dict);
229 
230     pthread_mutex_destroy(&dict->lock);
231     pthread_rwlock_destroy(&dict->rw_alloc_lock);
232 
233     dict->cur_usr->free(dict->cur_usr, dict);
234 }
235 
glz_enc_dictionary_get_size(GlzEncDictContext * opaque_dict)236 uint32_t glz_enc_dictionary_get_size(GlzEncDictContext *opaque_dict)
237 {
238     SharedDictionary *dict = (SharedDictionary *)opaque_dict;
239 
240     if (!opaque_dict) {
241         return 0;
242     }
243     return dict->window.size_limit;
244 }
245 
246 /* doesn't call the remove image callback */
glz_enc_dictionary_remove_image(GlzEncDictContext * opaque_dict,GlzEncDictImageContext * opaque_image,GlzEncoderUsrContext * usr)247 void glz_enc_dictionary_remove_image(GlzEncDictContext *opaque_dict,
248                                      GlzEncDictImageContext *opaque_image,
249                                      GlzEncoderUsrContext *usr)
250 {
251     SharedDictionary *dict = (SharedDictionary *)opaque_dict;
252     WindowImage *image = (WindowImage *)opaque_image;
253     dict->cur_usr = usr;
254     GLZ_ASSERT(dict->cur_usr, opaque_image && opaque_dict);
255 
256     glz_dictionary_window_kill_image(dict, image);
257 }
258 
259 /***********************************************************************************
260  Mutators of the window. Should be called by the encoder before and after encoding.
261  ***********************************************************************************/
262 
__get_pixels_num(LzImageType image_type,unsigned int num_lines,int stride)263 static inline int __get_pixels_num(LzImageType image_type, unsigned int num_lines, int stride)
264 {
265     if (IS_IMAGE_TYPE_RGB[image_type]) {
266         return num_lines * stride / RGB_BYTES_PER_PIXEL[image_type];
267     }
268 
269     return num_lines * stride * PLT_PIXELS_PER_BYTE[image_type];
270 }
271 
__glz_dictionary_window_segs_realloc(SharedDictionary * dict)272 static void __glz_dictionary_window_segs_realloc(SharedDictionary *dict)
273 {
274     WindowImageSegment *new_segs;
275     uint32_t new_quota = (MAX_IMAGE_SEGS_NUM < (dict->window.segs_quota * 2)) ?
276         MAX_IMAGE_SEGS_NUM : (dict->window.segs_quota * 2);
277     WindowImageSegment *seg;
278     uint32_t i;
279 
280     pthread_rwlock_wrlock(&dict->rw_alloc_lock);
281 
282     if (dict->window.segs_quota == MAX_IMAGE_SEGS_NUM) {
283         dict->cur_usr->error(dict->cur_usr, "overflow in image segments window\n");
284     }
285 
286     new_segs = (WindowImageSegment*)dict->cur_usr->malloc(
287             dict->cur_usr, sizeof(WindowImageSegment) * new_quota);
288 
289     if (!new_segs) {
290         dict->cur_usr->error(dict->cur_usr,
291                              "realloc of dictionary window failed\n");
292     }
293 
294     memcpy(new_segs, dict->window.segs,
295            sizeof(WindowImageSegment) * dict->window.segs_quota);
296 
297     // resetting the new elements
298     for (i = dict->window.segs_quota, seg = new_segs + i; i < new_quota; i++, seg++) {
299         seg->image = NULL;
300         seg->lines = NULL;
301         seg->lines_end = NULL;
302         seg->pixels_num = 0;
303         seg->pixels_so_far = 0;
304         seg->next = i + 1;
305     }
306     new_segs[new_quota - 1].next = dict->window.free_segs_head;
307     dict->window.free_segs_head = dict->window.segs_quota;
308 
309     dict->cur_usr->free(dict->cur_usr, dict->window.segs);
310     dict->window.segs = new_segs;
311     dict->window.segs_quota = new_quota;
312 
313     pthread_rwlock_unlock(&dict->rw_alloc_lock);
314 }
315 
316 /* NOTE - it also updates the used_images_list*/
__glz_dictionary_window_alloc_image(SharedDictionary * dict)317 static WindowImage *__glz_dictionary_window_alloc_image(SharedDictionary *dict)
318 {
319     WindowImage *ret;
320 
321     if (dict->window.free_images) {
322         ret = dict->window.free_images;
323         dict->window.free_images = ret->next;
324     } else {
325         if (!(ret = (WindowImage *)dict->cur_usr->malloc(dict->cur_usr,
326                                                          sizeof(*ret)))) {
327             return NULL;
328         }
329     }
330 
331     ret->next = NULL;
332     if (dict->window.used_images_tail) {
333         dict->window.used_images_tail->next = ret;
334     }
335     dict->window.used_images_tail = ret;
336 
337     if (!dict->window.used_images_head) {
338         dict->window.used_images_head = ret;
339     }
340     return ret;
341 }
342 
343 /* NOTE - it doesn't update the used_segs list*/
__glz_dictionary_window_alloc_image_seg(SharedDictionary * dict)344 static uint32_t __glz_dictionary_window_alloc_image_seg(SharedDictionary *dict)
345 {
346     uint32_t seg_id;
347     WindowImageSegment *seg;
348 
349     // TODO: when is it best to realloc? when full or when half full?
350     if (dict->window.free_segs_head == NULL_IMAGE_SEG_ID) {
351         __glz_dictionary_window_segs_realloc(dict);
352     }
353 
354     GLZ_ASSERT(dict->cur_usr, dict->window.free_segs_head != NULL_IMAGE_SEG_ID);
355 
356     seg_id = dict->window.free_segs_head;
357     seg = dict->window.segs + seg_id;
358     dict->window.free_segs_head = seg->next;
359 
360     return seg_id;
361 }
362 
363 /* moves image to free list and "kill" it. Calls the free_image callback if was alive. */
__glz_dictionary_window_free_image(SharedDictionary * dict,WindowImage * image)364 static inline void __glz_dictionary_window_free_image(SharedDictionary *dict, WindowImage *image)
365 {
366     if (image->is_alive) {
367         dict->cur_usr->free_image(dict->cur_usr, image->usr_context);
368     }
369     image->is_alive = FALSE;
370     image->next = dict->window.free_images;
371     dict->window.free_images = image;
372 }
373 
374 /* moves all the segments that were associated with the images to the free segments */
__glz_dictionary_window_free_image_segs(SharedDictionary * dict,WindowImage * image)375 static inline void __glz_dictionary_window_free_image_segs(SharedDictionary *dict,
376                                                            WindowImage *image)
377 {
378     uint32_t old_free_head = dict->window.free_segs_head;
379     uint32_t seg_id, next_seg_id;
380 
381     GLZ_ASSERT(dict->cur_usr, image->first_seg != NULL_IMAGE_SEG_ID);
382     dict->window.free_segs_head = image->first_seg;
383 
384     // retrieving the last segment of the image
385     for (seg_id = image->first_seg, next_seg_id = dict->window.segs[seg_id].next;
386          (next_seg_id != NULL_IMAGE_SEG_ID) && (dict->window.segs[next_seg_id].image == image);
387          seg_id = next_seg_id, next_seg_id = dict->window.segs[seg_id].next) {
388     }
389 
390     // concatenate the free list
391     dict->window.segs[seg_id].next = old_free_head;
392 }
393 
394 /* Returns the logical head of the window after we add an image with the give size to its tail.
395    Returns NULL when the window is empty, of when we have to empty the window in order
396    to insert the new image. */
glz_dictionary_window_get_new_head(SharedDictionary * dict,int new_image_size)397 static WindowImage *glz_dictionary_window_get_new_head(SharedDictionary *dict, int new_image_size)
398 {
399     uint32_t cur_win_size;
400     WindowImage *cur_head;
401 
402     if ((uint32_t)new_image_size > dict->window.size_limit) {
403         dict->cur_usr->error(dict->cur_usr, "image is bigger than window\n");
404     }
405 
406     GLZ_ASSERT(dict->cur_usr, new_image_size < dict->window.size_limit)
407 
408     // the window is empty
409     if (!dict->window.used_images_head) {
410         return NULL;
411     }
412 
413     GLZ_ASSERT(dict->cur_usr, dict->window.used_segs_head != NULL_IMAGE_SEG_ID);
414     GLZ_ASSERT(dict->cur_usr, dict->window.used_segs_tail != NULL_IMAGE_SEG_ID);
415 
416     // used_segs_head is the latest logical head (the physical head may precede it)
417     cur_head = dict->window.segs[dict->window.used_segs_head].image;
418     cur_win_size = dict->window.segs[dict->window.used_segs_tail].pixels_num +
419         dict->window.segs[dict->window.used_segs_tail].pixels_so_far -
420         dict->window.segs[dict->window.used_segs_head].pixels_so_far;
421 
422     while ((cur_win_size + new_image_size) > dict->window.size_limit) {
423         GLZ_ASSERT(dict->cur_usr, cur_head);
424         cur_win_size -= cur_head->size;
425         cur_head = cur_head->next;
426     }
427 
428     return cur_head;
429 }
430 
glz_dictionary_is_in_use(SharedDictionary * dict)431 static inline bool glz_dictionary_is_in_use(SharedDictionary *dict)
432 {
433     uint32_t i = 0;
434     for (i = 0; i < dict->max_encoders; i++) {
435         if (dict->window.encoders_heads[i] != NULL_IMAGE_SEG_ID) {
436             return TRUE;
437         }
438     }
439     return FALSE;
440 }
441 
442 /* remove from the window (and free relevant data) the images between the oldest physical head
443    (inclusive) and the end_image (exclusive). If end_image is NULL, empties the window*/
glz_dictionary_window_remove_head(SharedDictionary * dict,uint32_t encoder_id,WindowImage * end_image)444 static void glz_dictionary_window_remove_head(SharedDictionary *dict, uint32_t encoder_id,
445                                               WindowImage *end_image)
446 {
447     // note that the segs list heads (one per encoder) may be different than the
448     // used_segs_head and it is updated somewhere else
449     while (dict->window.used_images_head != end_image) {
450         WindowImage *image = dict->window.used_images_head;
451 
452         __glz_dictionary_window_free_image_segs(dict, image);
453         dict->window.used_images_head = image->next;
454         __glz_dictionary_window_free_image(dict, image);
455     }
456 
457     if (!dict->window.used_images_head) {
458         dict->window.used_segs_head = NULL_IMAGE_SEG_ID;
459         dict->window.used_segs_tail = NULL_IMAGE_SEG_ID;
460         dict->window.used_images_tail = NULL;
461     } else {
462         dict->window.used_segs_head = end_image->first_seg;
463     }
464 }
465 
glz_dictionary_window_alloc_image_seg(SharedDictionary * dict,WindowImage * image,int size,int stride,uint8_t * lines,unsigned int num_lines)466 static uint32_t glz_dictionary_window_alloc_image_seg(SharedDictionary *dict, WindowImage* image,
467                                                       int size, int stride,
468                                                       uint8_t *lines, unsigned int num_lines)
469 {
470     uint32_t seg_id = __glz_dictionary_window_alloc_image_seg(dict);
471     WindowImageSegment *seg = &dict->window.segs[seg_id];
472 
473     seg->image = image;
474     seg->lines = lines;
475     seg->lines_end = lines + num_lines * stride;
476     seg->pixels_num = size;
477     seg->pixels_so_far = dict->window.pixels_so_far;
478     dict->window.pixels_so_far += seg->pixels_num;
479 
480     seg->next = NULL_IMAGE_SEG_ID;
481 
482     return seg_id;
483 }
484 
glz_dictionary_window_add_image(SharedDictionary * dict,LzImageType image_type,int image_size,int image_height,int image_stride,uint8_t * first_lines,unsigned int num_first_lines,GlzUsrImageContext * usr_image_context)485 static WindowImage *glz_dictionary_window_add_image(SharedDictionary *dict, LzImageType image_type,
486                                                     int image_size, int image_height,
487                                                     int image_stride, uint8_t *first_lines,
488                                                     unsigned int num_first_lines,
489                                                     GlzUsrImageContext *usr_image_context)
490 {
491     unsigned int num_lines = num_first_lines;
492     unsigned int row;
493     uint32_t seg_id;
494     uint32_t prev_seg_id = 0;
495     uint8_t* lines = first_lines;
496     // alloc image info,update used head tail,  if used_head null - update  head
497     WindowImage *image = __glz_dictionary_window_alloc_image(dict);
498     if (!image) {
499         dict->cur_usr->error(dict->cur_usr, "glz-dictionary failed to allocate an image\n");
500     }
501     image->id = dict->last_image_id++;
502     image->size = image_size;
503     image->type = image_type;
504     image->usr_context = usr_image_context;
505 
506     if (num_lines <= 0) {
507         num_lines = dict->cur_usr->more_lines(dict->cur_usr, &lines);
508         if (num_lines <= 0) {
509             dict->cur_usr->error(dict->cur_usr, "more lines failed\n");
510         }
511     }
512 
513     for (row = 0;;) {
514         seg_id = glz_dictionary_window_alloc_image_seg(dict, image,
515                                                        image_size * num_lines / image_height,
516                                                        image_stride,
517                                                        lines, num_lines);
518         if (row == 0) {
519             image->first_seg = seg_id;
520         } else {
521             dict->window.segs[prev_seg_id].next = seg_id;
522         }
523 
524         row += num_lines;
525         if (row < (uint32_t)image_height) {
526             num_lines = dict->cur_usr->more_lines(dict->cur_usr, &lines);
527             if (num_lines <= 0) {
528                 dict->cur_usr->error(dict->cur_usr, "more lines failed\n");
529             }
530         } else {
531             break;
532         }
533         prev_seg_id = seg_id;
534     }
535 
536     if (dict->window.used_segs_tail == NULL_IMAGE_SEG_ID) {
537         dict->window.used_segs_head = image->first_seg;
538         dict->window.used_segs_tail = seg_id;
539     } else {
540         int prev_tail = dict->window.used_segs_tail;
541 
542         // The used segs may be in use by another thread which is during encoding
543         // (read-only use - when going over the segs of an image,
544         // see glz_encode_tmpl::compress).
545         // Thus, the 'next' field of the list's tail can be accessed only
546         // after all the new tail's data was set. Note that we are relying on
547         // an atomic assignment (32 bit variable).
548         // For the other thread that may read 'next' of the old tail, NULL_IMAGE_SEG_ID
549         // is equivalent to a segment with an image id that is different
550         // from the image id of the tail, so we don't need to further protect this field.
551         dict->window.segs[prev_tail].next = image->first_seg;
552         dict->window.used_segs_tail = seg_id;
553     }
554     image->is_alive = TRUE;
555 
556     return image;
557 }
558 
glz_dictionary_pre_encode(uint32_t encoder_id,GlzEncoderUsrContext * usr,SharedDictionary * dict,LzImageType image_type,int image_width,int image_height,int image_stride,uint8_t * first_lines,unsigned int num_first_lines,GlzUsrImageContext * usr_image_context,uint32_t * image_head_dist)559 WindowImage *glz_dictionary_pre_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
560                                        SharedDictionary *dict, LzImageType image_type,
561                                        int image_width, int image_height, int image_stride,
562                                        uint8_t *first_lines, unsigned int num_first_lines,
563                                        GlzUsrImageContext *usr_image_context,
564                                        uint32_t *image_head_dist)
565 {
566     WindowImage *new_win_head, *ret;
567     int image_size;
568 
569 
570     pthread_mutex_lock(&dict->lock);
571 
572     dict->cur_usr = usr;
573     GLZ_ASSERT(dict->cur_usr, dict->window.encoders_heads[encoder_id] == NULL_IMAGE_SEG_ID);
574 
575     image_size = __get_pixels_num(image_type, image_height, image_stride);
576     new_win_head = glz_dictionary_window_get_new_head(dict, image_size);
577 
578     if (!glz_dictionary_is_in_use(dict)) {
579         glz_dictionary_window_remove_head(dict, encoder_id, new_win_head);
580     }
581 
582     ret = glz_dictionary_window_add_image(dict, image_type, image_size, image_height, image_stride,
583                                           first_lines, num_first_lines, usr_image_context);
584 
585     if (new_win_head) {
586         dict->window.encoders_heads[encoder_id] = new_win_head->first_seg;
587         *image_head_dist = (uint32_t)(ret->id - new_win_head->id); // shouldn't be greater than 32
588                                                                    // bit because the window size is
589                                                                    // limited to 2^25
590     } else {
591         dict->window.encoders_heads[encoder_id] = ret->first_seg;
592         *image_head_dist = 0;
593     }
594 
595 
596     // update encoders head  (the other heads were already updated)
597     pthread_mutex_unlock(&dict->lock);
598     pthread_rwlock_rdlock(&dict->rw_alloc_lock);
599     return ret;
600 }
601 
glz_dictionary_post_encode(uint32_t encoder_id,GlzEncoderUsrContext * usr,SharedDictionary * dict)602 void glz_dictionary_post_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
603                                 SharedDictionary *dict)
604 {
605     uint32_t i;
606     uint32_t early_head_seg = NULL_IMAGE_SEG_ID;
607     uint32_t this_encoder_head_seg;
608 
609     pthread_rwlock_unlock(&dict->rw_alloc_lock);
610     pthread_mutex_lock(&dict->lock);
611     dict->cur_usr = usr;
612 
613     GLZ_ASSERT(dict->cur_usr, dict->window.encoders_heads[encoder_id] != NULL_IMAGE_SEG_ID);
614     // get the earliest head in use (not including this encoder head)
615     for (i = 0; i < dict->max_encoders; i++) {
616         if (i != encoder_id) {
617             if (IMAGE_SEG_IS_EARLIER(dict, dict->window.encoders_heads[i], early_head_seg)) {
618                 early_head_seg = dict->window.encoders_heads[i];
619             }
620         }
621     }
622 
623     // possible only if early_head_seg == NULL
624     if (IMAGE_SEG_IS_EARLIER(dict, dict->window.used_segs_head, early_head_seg)) {
625         early_head_seg = dict->window.used_segs_head;
626     }
627 
628     this_encoder_head_seg = dict->window.encoders_heads[encoder_id];
629 
630     GLZ_ASSERT(dict->cur_usr, early_head_seg != NULL_IMAGE_SEG_ID);
631 
632     if (IMAGE_SEG_IS_EARLIER(dict, this_encoder_head_seg, early_head_seg)) {
633         GLZ_ASSERT(dict->cur_usr,
634                    this_encoder_head_seg == dict->window.used_images_head->first_seg);
635         glz_dictionary_window_remove_head(dict, encoder_id,
636                                           dict->window.segs[early_head_seg].image);
637     }
638 
639 
640     dict->window.encoders_heads[encoder_id] = NULL_IMAGE_SEG_ID;
641     pthread_mutex_unlock(&dict->lock);
642 }
643