encode.c 21.7 KB
Newer Older
1
/**
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
2
 * @file encode.c
3 4 5 6
 * @author Mathis Rosenhauer, Deutsches Klimarechenzentrum
 * @section DESCRIPTION
 *
 * Adaptive Entropy Encoder
7
 * Based on CCSDS documents 121.0-B-2 and 120.0-G-2
8 9
 *
 */
10 11 12

#include <stdio.h>
#include <stdlib.h>
13
#include <unistd.h>
14 15 16
#include <inttypes.h>
#include <string.h>

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
17 18 19
#include "libaec.h"
#include "encode.h"
#include "encode_accessors.h"
20

21
/* Marker for Remainder Of Segment condition in zero block encoding */
22
#define ROS -1
23 24 25

#define MIN(a, b) (((a) < (b))? (a): (b))

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
26 27 28 29 30 31 32 33 34 35
static int m_get_block(aec_streamp strm);
static int m_get_block_cautious(aec_streamp strm);
static int m_check_zero_block(aec_streamp strm);
static int m_select_code_option(aec_streamp strm);
static int m_flush_block(aec_streamp strm);
static int m_flush_block_cautious(aec_streamp strm);
static int m_encode_splitting(aec_streamp strm);
static int m_encode_uncomp(aec_streamp strm);
static int m_encode_se(aec_streamp strm);
static int m_encode_zero(aec_streamp strm);
36 37 38 39 40 41

/*
 *
 * Bit emitters
 *
 */
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
42

43
static inline void emit(encode_state *state, uint32_t data, int bits)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
44
{
45
    if (bits <= state->bit_p)
46
    {
47 48
        state->bit_p -= bits;
        *state->cds_p += data << state->bit_p;
49 50 51
    }
    else
    {
52
        bits -= state->bit_p;
53
        *state->cds_p++ += (uint64_t)data >> bits;
54 55

        while (bits & ~7)
56
        {
57
            bits -= 8;
58
            *state->cds_p++ = data >> bits;
59
        }
60

61 62
        state->bit_p = 8 - bits;
        *state->cds_p = data << state->bit_p;
63
    }
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
64 65
}

66
static inline void emitfs(encode_state *state, int fs)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
67
{
68 69
    /**
       Emits a fundamental sequence.
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
70

71 72
       fs zero bits followed by one 1 bit.
     */
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
73

74 75
    for(;;)
    {
76
        if (fs < state->bit_p)
77
        {
78 79
            state->bit_p -= fs + 1;
            *state->cds_p += 1 << state->bit_p;
80 81 82 83
            break;
        }
        else
        {
84 85 86
            fs -= state->bit_p;
            *++state->cds_p = 0;
            state->bit_p = 8;
87 88
        }
    }
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
89
}
90

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
91
#define EMITBLOCK(ref)                                          \
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
92
    static inline void emitblock_##ref(aec_streamp strm, int k) \
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
    {                                                           \
        int b;                                                  \
        uint64_t a;                                             \
        encode_state *state = strm->state;                      \
        uint32_t *in = state->block_p + ref;                    \
        uint32_t *in_end = state->block_p + strm->block_size;   \
        uint64_t mask = (1ULL << k) - 1;                        \
        uint8_t *o = state->cds_p;                              \
        int p = state->bit_p;                                   \
                                                                \
        a = *o;                                                 \
                                                                \
        while(in < in_end)                                      \
        {                                                       \
            a <<= 56;                                           \
            p = (p % 8) + 56;                                   \
                                                                \
            while (p > k && in < in_end)                        \
            {                                                   \
                p -= k;                                         \
                a += ((uint64_t)(*in++) & mask) << p;           \
            }                                                   \
                                                                \
            for (b = 56; b > (p & ~7); b -= 8)                  \
                *o++ = a >> b;                                  \
            a >>= b;                                            \
        }                                                       \
                                                                \
        *o = a;                                                 \
        state->cds_p = o;                                       \
        state->bit_p = p % 8;                                   \
    }

EMITBLOCK(0);
EMITBLOCK(1);

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
129
static void preprocess_unsigned(aec_streamp strm)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
130 131
{
    int i;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
132
    int64_t theta, Delta, prev;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
133 134
    encode_state *state = strm->state;

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
135 136 137 138
    /* Insert reference samples into first block of Reference Sample
     * Interval.
     */
    prev = state->block_buf[0];
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
139

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
140
    for (i = 1; i < strm->rsi * strm->block_size; i++)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
141
    {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
142 143 144
        theta = MIN(prev, state->xmax - prev);
        Delta = (int64_t)state->block_buf[i] - prev;
        prev = state->block_buf[i];
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
145

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
146
        if (0 <= Delta && Delta <= theta)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
147
        {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
148 149 150 151 152 153 154 155 156 157 158
            state->block_buf[i] = 2 * Delta;
        }
        else if (-theta <= Delta && Delta < 0)
        {
            state->block_buf[i] = 2
                * (Delta < 0 ? -(uint64_t)Delta : Delta) - 1;
        }
        else
        {
            state->block_buf[i] = theta
                + (Delta < 0 ? -(uint64_t)Delta : Delta);
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
159 160 161 162
        }
    }
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
163
static void preprocess_signed(aec_streamp strm)
164
{
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
165 166
    int i, m;
    int64_t theta, Delta, prev, sample;
167
    encode_state *state = strm->state;
168

169 170 171
    /* Insert reference samples into first block of Reference Sample
     * Interval.
     */
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
172 173
    m = 64 - strm->bit_per_sample;
    prev = ((int64_t)state->block_buf[0] << m) >> m;
174

175
    for (i = 1; i < strm->rsi * strm->block_size; i++)
176
    {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
177 178 179 180 181
        theta = MIN(prev - state->xmin, state->xmax - prev);
        sample = ((int64_t)state->block_buf[i] << m) >> m;
        Delta = sample - prev;
        prev = sample;

182 183
        if (0 <= Delta && Delta <= theta)
        {
184
            state->block_buf[i] = 2 * Delta;
185 186 187
        }
        else if (-theta <= Delta && Delta < 0)
        {
188 189
            state->block_buf[i] = 2
                * (Delta < 0 ? -(uint64_t)Delta : Delta) - 1;
190
        }
191
        else
192
        {
193 194
            state->block_buf[i] = theta
                + (Delta < 0 ? -(uint64_t)Delta : Delta);
195
        }
196
    }
197
}
198

199 200 201 202 203 204
/*
 *
 * FSM functions
 *
 */

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
205
static int m_get_block(aec_streamp strm)
206 207 208
{
    encode_state *state = strm->state;

209
    if (strm->avail_out > state->cds_len)
210
    {
211
        if (!state->direct_out)
212
        {
213 214 215
            state->direct_out = 1;
            *strm->next_out = *state->cds_p;
            state->cds_p = strm->next_out;
216
        }
217 218 219
    }
    else
    {
220
        if (state->zero_blocks == 0 || state->direct_out)
221 222
        {
            /* copy leftover from last block */
223 224
            *state->cds_buf = *state->cds_p;
            state->cds_p = state->cds_buf;
225
        }
226
        state->direct_out = 0;
227
    }
228

229
    if (state->blocks_avail == 0)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
230
    {
231 232 233
        state->ref = 1;
        state->blocks_avail = strm->rsi - 1;
        state->block_p = state->block_buf;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
234

235 236 237
        if (strm->avail_in >= state->block_len * strm->rsi)
        {
            state->get_block(strm);
238

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
239
            if (strm->flags & AEC_DATA_PREPROCESS)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
240
                state->preprocess(strm);
241

242 243 244 245 246 247 248
            return m_check_zero_block(strm);
        }
        else
        {
            state->i = 0;
            state->mode = m_get_block_cautious;
        }
249 250
    }
    else
251
    {
252 253 254 255
        state->ref = 0;
        state->block_p += strm->block_size;
        state->blocks_avail--;
        return m_check_zero_block(strm);
256
    }
257
    return M_CONTINUE;
258 259
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
260
static int m_get_block_cautious(aec_streamp strm)
261
{
262
    int j;
263
    encode_state *state = strm->state;
264

265
    do
266
    {
267 268 269 270 271
        if (strm->avail_in > 0)
        {
            state->block_buf[state->i] = state->get_sample(strm);
        }
        else
272
        {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
273
            if (state->flush == AEC_FLUSH)
274 275 276
            {
                if (state->i > 0)
                {
277 278 279
                    /* Pad block with last sample if we have a partial
                     * block.
                     */
280 281 282
                    for (j = state->i; j < strm->rsi * strm->block_size; j++)
                        state->block_buf[j] = state->block_buf[state->i - 1];
                    state->i = strm->rsi * strm->block_size;
283 284 285 286 287 288 289 290 291
                }
                else
                {
                    if (state->zero_blocks)
                    {
                        /* Output any remaining zero blocks */
                        state->mode = m_encode_zero;
                        return M_CONTINUE;
                    }
292 293 294 295

                    /* Pad last output byte with 0 bits if user wants
                     * to flush, i.e. we got all input there is.
                     */
296 297 298
                    emit(state, 0, state->bit_p);
                    if (state->direct_out == 0)
                        *strm->next_out++ = *state->cds_p;
299 300
                    strm->avail_out--;
                    strm->total_out++;
301

302 303 304 305 306 307 308
                    return M_EXIT;
                }
            }
            else
            {
                return M_EXIT;
            }
309 310
        }
    }
311
    while (++state->i < strm->rsi * strm->block_size);
312

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
313
    if (strm->flags & AEC_DATA_PREPROCESS)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
314
        state->preprocess(strm);
315 316

    return m_check_zero_block(strm);
317 318
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
319
static inline int m_check_zero_block(aec_streamp strm)
320
{
321 322 323 324
    int i;
    encode_state *state = strm->state;

    i = state->ref;
325
    while(i < strm->block_size && state->block_p[i] == 0)
326 327 328
        i++;

    if (i == strm->block_size)
329
    {
330 331 332 333
        /* remember ref on first zero block */
        if (state->zero_blocks == 0)
        {
            state->zero_ref = state->ref;
334
            state->zero_ref_sample = state->block_p[0];
335
        }
336

337
        state->zero_blocks++;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
338

339
        if ((strm->rsi - state->blocks_avail) % 64 == 0)
340 341 342 343 344 345 346 347 348 349
        {
            if (state->zero_blocks > 4)
                state->zero_blocks = ROS;
            state->mode = m_encode_zero;
            return M_CONTINUE;
        }
        state->mode = m_get_block;
        return M_CONTINUE;
    }
    else if (state->zero_blocks)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
350
    {
351 352 353 354
        /* The current block isn't zero but we have to emit a previous
         * zero block first. The current block will be handled
         * later.
         */
355 356
        state->block_p -= strm->block_size;
        state->blocks_avail++;
357 358
        state->mode = m_encode_zero;
        return M_CONTINUE;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
359
    }
360 361
    state->mode = m_select_code_option;
    return M_CONTINUE;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
362 363
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
364
static inline int m_select_code_option(aec_streamp strm)
365
{
366
    int i, j, k, this_bs, looked_bothways, direction;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
367 368 369
    uint64_t split_len, uncomp_len;
    uint64_t split_len_min, fs_len;
    uint64_t d, se_len;
370
    encode_state *state = strm->state;
371

372 373
    /* Length of this block minus reference sample (if present) */
    this_bs = strm->block_size - state->ref;
374

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
375 376
    split_len_min = UINT64_MAX;
    i = k = state->k;
377 378
    direction = 1;
    looked_bothways = 0;
379

380
    /* Starting with splitting position of last block. Look left and
381 382
     * possibly right to find new minimum.
     */
383 384
    for (;;)
    {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
385 386 387 388 389 390 391
        fs_len = (uint64_t)(state->block_p[1] >> i)
            + (uint64_t)(state->block_p[2] >> i)
            + (uint64_t)(state->block_p[3] >> i)
            + (uint64_t)(state->block_p[4] >> i)
            + (uint64_t)(state->block_p[5] >> i)
            + (uint64_t)(state->block_p[6] >> i)
            + (uint64_t)(state->block_p[7] >> i);
392

393 394 395 396 397 398 399 400 401 402 403 404
        if (strm->block_size > 8)
            for (j = 1; j < strm->block_size / 8; j++)
                fs_len +=
                    (uint64_t)(state->block_p[j * 8 + 0] >> i)
                    + (uint64_t)(state->block_p[j * 8 + 1] >> i)
                    + (uint64_t)(state->block_p[j * 8 + 2] >> i)
                    + (uint64_t)(state->block_p[j * 8 + 3] >> i)
                    + (uint64_t)(state->block_p[j * 8 + 4] >> i)
                    + (uint64_t)(state->block_p[j * 8 + 5] >> i)
                    + (uint64_t)(state->block_p[j * 8 + 6] >> i)
                    + (uint64_t)(state->block_p[j * 8 + 7] >> i);

405
        if (state->ref == 0)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
406
            fs_len += (uint64_t)(state->block_p[0] >> i);
407 408 409 410

        split_len = fs_len + this_bs * (i + 1);

        if (split_len < split_len_min)
411
        {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
412
            if (split_len_min < UINT64_MAX)
413
            {
414
                /* We are moving towards the minimum so it cant be in
415 416
                 * the other direction.
                 */
417
                looked_bothways = 1;
418
            }
419 420
            split_len_min = split_len;
            k = i;
421

422
            if (direction == 1)
423
            {
424
                if (fs_len < this_bs)
425
                {
426
                    /* Next can't get better because what we lose by
427 428 429 430
                     * additional uncompressed bits isn't compensated
                     * by a smaller FS part. Vice versa if we are
                     * coming from the other direction.
                     */
431
                    if (looked_bothways)
432
                    {
433
                        break;
434
                    }
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
435 436
                    else
                    {
437 438 439
                        direction = -direction;
                        looked_bothways = 1;
                        i = state->k;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
440
                    }
441 442 443
                }
                else
                {
444 445 446 447 448
                    while (fs_len > 5 * this_bs)
                    {
                        i++;
                        fs_len /= 5;
                    }
449 450
                }
            }
451
            else if (fs_len > this_bs)
452
            {
453
                /* Since we started looking the other way there is no
454 455
                 * need to turn back.
                 */
456 457 458 459 460
                break;
            }
        }
        else
        {
461 462 463
            /* Stop looking for better option if we don't see any
             * improvement.
             */
464
                if (looked_bothways)
465
                {
466
                    break;
467 468 469
                }
                else
                {
470 471 472
                    direction = -direction;
                    looked_bothways = 1;
                    i = state->k;
473
                }
474 475 476 477 478 479
        }
        if (i + direction < 0
            || i + direction >= strm->bit_per_sample - 2)
        {
            if (looked_bothways)
                break;
480

481 482 483 484
            direction = -direction;
            looked_bothways = 1;
            i = state->k;
        }
485

486 487 488
        i += direction;
    }
    state->k = k;
489

490 491 492 493
    /* Count bits for 2nd extension */
    se_len = 1;
    for (i = 0; i < strm->block_size; i+= 2)
    {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
494 495
        d = (uint64_t)state->block_p[i]
            + (uint64_t)state->block_p[i + 1];
496 497 498
        /* we have to worry about overflow here */
        if (d > split_len_min)
        {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
499
            se_len = UINT64_MAX;
500 501 502 503
            break;
        }
        else
        {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
504
            se_len += d * (d + 1) / 2 + (uint64_t)state->block_p[i + 1];
505 506
        }
    }
507

508 509
    /* Length of uncompressed block */
    uncomp_len = this_bs * strm->bit_per_sample;
510

511 512 513
    /* Decide which option to use */
    if (split_len_min < uncomp_len)
    {
514
        if (split_len_min < se_len)
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
        {
            /* Splitting won - the most common case. */
            return m_encode_splitting(strm);
        }
        else
        {
            return m_encode_se(strm);
        }
    }
    else
    {
        if (uncomp_len <= se_len)
        {
            return m_encode_uncomp(strm);
        }
        else
        {
            return m_encode_se(strm);
        }
    }
}
536

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
537
static inline int m_encode_splitting(aec_streamp strm)
538 539 540 541
{
    int i;
    encode_state *state = strm->state;
    int k = state->k;
542

543
    emit(state, k + 1, state->id_len);
544

545
    if (state->ref)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
546
    {
547
        emit(state, state->block_p[0], strm->bit_per_sample);
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
548 549 550 551 552 553 554 555 556 557
        for (i = 1; i < strm->block_size; i++)
            emitfs(state, state->block_p[i] >> k);
        if (k) emitblock_1(strm, k);
    }
    else
    {
        for (i = 0; i < strm->block_size; i++)
            emitfs(state, state->block_p[i] >> k);
        if (k) emitblock_0(strm, k);
    }
558

559 560
    return m_flush_block(strm);
}
561

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
562
static inline int m_encode_uncomp(aec_streamp strm)
563 564
{
    encode_state *state = strm->state;
565

566
    emit(state, (1 << state->id_len) - 1, state->id_len);
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
567
    emitblock_0(strm, strm->bit_per_sample);
568

569 570
    return m_flush_block(strm);
}
571

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
572
static inline int m_encode_se(aec_streamp strm)
573 574
{
    int i;
575
    uint32_t d;
576
    encode_state *state = strm->state;
577

578 579
    emit(state, 1, state->id_len + 1);
    if (state->ref)
580
        emit(state, state->block_p[0], strm->bit_per_sample);
581

582 583
    for (i = 0; i < strm->block_size; i+= 2)
    {
584 585
        d = state->block_p[i] + state->block_p[i + 1];
        emitfs(state, d * (d + 1) / 2 + state->block_p[i + 1]);
586
    }
587

588 589
    return m_flush_block(strm);
}
590

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
591
static inline int m_encode_zero(aec_streamp strm)
592 593
{
    encode_state *state = strm->state;
594

595
    emit(state, 0, state->id_len + 1);
596

597 598
    if (state->zero_ref)
        emit(state, state->zero_ref_sample, strm->bit_per_sample);
599

600 601 602 603 604 605
    if (state->zero_blocks == ROS)
        emitfs(state, 4);
    else if (state->zero_blocks >= 5)
        emitfs(state, state->zero_blocks);
    else
        emitfs(state, state->zero_blocks - 1);
606

607 608 609
    state->zero_blocks = 0;
    return m_flush_block(strm);
}
610

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
611
static inline int m_flush_block(aec_streamp strm)
612 613 614
{
    int n;
    encode_state *state = strm->state;
615

616
    if (state->direct_out)
617
    {
618
        n = state->cds_p - strm->next_out;
619 620 621 622 623 624
        strm->next_out += n;
        strm->avail_out -= n;
        strm->total_out += n;
        state->mode = m_get_block;
        return M_CONTINUE;
    }
625

626 627 628 629 630
    state->i = 0;
    state->mode = m_flush_block_cautious;
    return M_CONTINUE;
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
631
static inline int m_flush_block_cautious(aec_streamp strm)
632 633 634 635
{
    encode_state *state = strm->state;

    /* Slow restartable flushing */
636
    while(state->cds_buf + state->i < state->cds_p)
637 638 639 640
    {
        if (strm->avail_out == 0)
            return M_EXIT;

641
        *strm->next_out++ = state->cds_buf[state->i];
642 643 644 645 646 647 648 649 650 651 652 653 654
        strm->avail_out--;
        strm->total_out++;
        state->i++;
    }
    state->mode = m_get_block;
    return M_CONTINUE;
}

/*
 *
 * API functions
 *
 */
655

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
656
int aec_encode_init(aec_streamp strm)
657
{
658
    int bs, bsi;
659 660 661 662
    encode_state *state;

    /* Some sanity checks */
    if (strm->bit_per_sample > 32 || strm->bit_per_sample == 0)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
663
        return AEC_CONF_ERROR;
664 665 666 667 668

    if (strm->block_size != 8
        && strm->block_size != 16
        && strm->block_size != 32
        && strm->block_size != 64)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
669
        return AEC_CONF_ERROR;
670 671

    if (strm->rsi > 4096)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
672
        return AEC_CONF_ERROR;
673 674 675 676 677

    /* Internal state for encoder */
    state = (encode_state *) malloc(sizeof(encode_state));
    if (state == NULL)
    {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
678
        return AEC_MEM_ERROR;
679 680 681 682
    }
    memset(state, 0, sizeof(encode_state));
    strm->state = state;

683 684 685 686 687
    bs = strm->block_size >> 3;
    bsi = 0;
    while (bs >>= 1)
        bsi++;

688 689
    if (strm->bit_per_sample > 16)
    {
690
        /* 24/32 input bit settings */
691 692
        state->id_len = 5;

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
693
        if (strm->bit_per_sample <= 24 && strm->flags & AEC_DATA_3BYTE)
694
        {
695
            state->block_len = 3 * strm->block_size;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
696
            if (strm->flags & AEC_DATA_MSB)
697 698 699 700 701 702
            {
                state->get_sample = get_msb_24;
                state->get_block = get_block_funcs_msb_24[bsi];
            }
            else
            {
703 704
                state->get_sample = get_lsb_24;
                state->get_block = get_block_funcs_lsb_24[bsi];
705
            }
706
        }
707
        else
708
        {
709
            state->block_len = 4 * strm->block_size;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
710
            if (strm->flags & AEC_DATA_MSB)
711
            {
712 713
                state->get_sample = get_msb_32;
                state->get_block = get_block_funcs_msb_32[bsi];
714 715 716 717 718 719 720
            }
            else
            {
                state->get_sample = get_lsb_32;
                state->get_block = get_block_funcs_lsb_32[bsi];
            }
        }
721 722 723 724 725
    }
    else if (strm->bit_per_sample > 8)
    {
        /* 16 bit settings */
        state->id_len = 4;
726
        state->block_len = 2 * strm->block_size;
727

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
728
        if (strm->flags & AEC_DATA_MSB)
729 730
        {
            state->get_sample = get_msb_16;
731
            state->get_block = get_block_funcs_msb_16[bsi];
732
        }
733
        else
734
        {
735
            state->get_sample = get_lsb_16;
736 737
            state->get_block = get_block_funcs_lsb_16[bsi];
        }
738 739 740 741 742
    }
    else
    {
        /* 8 bit settings */
        state->id_len = 3;
743
        state->block_len = strm->block_size;
744 745

        state->get_sample = get_8;
746
        state->get_block = get_block_funcs_8[bsi];
747 748
    }

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
749
    if (strm->flags & AEC_DATA_SIGNED)
750 751 752
    {
        state->xmin = -(1ULL << (strm->bit_per_sample - 1));
        state->xmax = (1ULL << (strm->bit_per_sample - 1)) - 1;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
753
        state->preprocess = preprocess_signed;
754 755 756 757 758
    }
    else
    {
        state->xmin = 0;
        state->xmax = (1ULL << strm->bit_per_sample) - 1;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
759
        state->preprocess = preprocess_unsigned;
760 761
    }

762 763 764
    state->block_buf = (uint32_t *)malloc(strm->rsi
                                         * strm->block_size
                                         * sizeof(uint32_t));
765
    if (state->block_buf == NULL)
766
    {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
767
        return AEC_MEM_ERROR;
768
    }
769
    state->block_p = state->block_buf;
770 771

    /* Largest possible block according to specs */
772 773 774
    state->cds_len = (5 + 64 * 32) / 8 + 3;
    state->cds_buf = (uint8_t *)malloc(state->cds_len);
    if (state->cds_buf == NULL)
775
    {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
776
        return AEC_MEM_ERROR;
777 778
    }

779 780 781
    strm->total_in = 0;
    strm->total_out = 0;

782 783 784
    state->cds_p = state->cds_buf;
    *state->cds_p = 0;
    state->bit_p = 8;
785 786
    state->mode = m_get_block;

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
787
    return AEC_OK;
788 789
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
790
int aec_encode(aec_streamp strm, int flush)
791 792 793 794 795
{
    /**
       Finite-state machine implementation of the adaptive entropy
       encoder.
    */
796
    int n;
797 798 799 800 801 802
    encode_state *state;
    state = strm->state;
    state->flush = flush;

    while (state->mode(strm) == M_CONTINUE);

803
    if (state->direct_out)
804
    {
805 806 807 808 809 810 811 812
        n = state->cds_p - strm->next_out;
        strm->next_out += n;
        strm->avail_out -= n;
        strm->total_out += n;

        *state->cds_buf = *state->cds_p;
        state->cds_p = state->cds_buf;
        state->direct_out = 0;
813
    }
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
814
    return AEC_OK;
815 816
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
817
int aec_encode_end(aec_streamp strm)
818 819 820
{
    encode_state *state = strm->state;

821 822
    free(state->block_buf);
    free(state->cds_buf);
823
    free(state);
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
824
    return AEC_OK;
825
}