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