decode.c 17.2 KB
Newer Older
1 2 3 4 5 6 7 8 9
/* Adaptive Entropy Decoder            */
/* CCSDS 121.0-B-1 and CCSDS 120.0-G-2 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
10
#include "libaec.h"
11

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

14
#define SAFE (strm->avail_in >= state->in_blklen        \
15
              && strm->avail_out >= state->out_blklen)
16 17 18 19

#define ROS 5

typedef struct internal_state {
20
    int id;            /* option ID */
21
    int id_len;        /* bit length of code option identification key */
22
    int *id_table;     /* table maps IDs to states */
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
23
    void (*put_sample)(aec_streamp, int64_t);
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
24
    int ref_int;       /* reference sample is every ref_int samples */
25
    int64_t last_out;  /* previous output for post-processing */
26 27 28
    int64_t xmin;      /* minimum integer for post-processing */
    int64_t xmax;      /* maximum integer for post-processing */
    int mode;          /* current mode of FSM */
29
    int in_blklen;     /* length of uncompressed input block
30
                          should be the longest possible block */
31
    int out_blklen;    /* length of output block in bytes */
32 33
    int n, i;          /* counter for samples */
    int64_t *block;    /* block buffer for split-sample options */
34 35
    int se;            /* set if second extension option is selected */
    uint64_t acc;      /* accumulator for currently used bit sequence */
36 37
    int bitp;          /* bit pointer to the next unused bit in accumulator */
    int fs;            /* last fundamental sequence in accumulator */
38
    int ref;           /* 1 if current block has reference sample */
39
    int pp;            /* 1 if postprocessor has to be used */
40
    int byte_per_sample;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
41
    size_t samples_out;
42 43 44
} decode_state;

/* decoding table for the second-extension option */
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
45 46
static const int second_extension[92][2] =
{
47 48 49 50 51 52 53
    {0, 0},
    {1, 1}, {1, 1},
    {2, 3}, {2, 3}, {2, 3},
    {3, 6}, {3, 6}, {3, 6}, {3, 6},
    {4, 10}, {4, 10}, {4, 10}, {4, 10}, {4, 10},
    {5, 15}, {5, 15}, {5, 15}, {5, 15}, {5, 15}, {5, 15},
    {6, 21}, {6, 21}, {6, 21}, {6, 21}, {6, 21}, {6, 21}, {6, 21},
54 55 56 57 58 59
    {7, 28}, {7, 28}, {7, 28}, {7, 28}, {7, 28}, {7, 28}, {7, 28}, {7, 28},
    {8, 36}, {8, 36}, {8, 36}, {8, 36}, {8, 36}, {8, 36}, {8, 36}, {8, 36}, {8, 36},
    {9, 45}, {9, 45}, {9, 45}, {9, 45}, {9, 45}, {9, 45}, {9, 45}, {9, 45}, {9, 45}, {9, 45},
    {10, 55}, {10, 55}, {10, 55}, {10, 55}, {10, 55}, {10, 55}, {10, 55}, {10, 55}, {10, 55}, {10, 55}, {10, 55},
    {11, 66}, {11, 66}, {11, 66}, {11, 66}, {11, 66}, {11, 66}, {11, 66}, {11, 66}, {11, 66}, {11, 66}, {11, 66}, {11, 66},
    {12, 78}, {12, 78}, {12, 78}, {12, 78}, {12, 78}, {12, 78}, {12, 78}, {12, 78}, {12, 78}, {12, 78}, {12, 78}, {12, 78}, {12, 78}
60 61 62 63
};

enum
{
64 65 66 67 68 69 70 71 72 73 74 75
    M_ID = 0,
    M_SPLIT,
    M_SPLIT_FS,
    M_SPLIT_OUTPUT,
    M_LOW_ENTROPY,
    M_LOW_ENTROPY_REF,
    M_ZERO_BLOCK,
    M_ZERO_OUTPUT,
    M_SE,
    M_SE_DECODE,
    M_UNCOMP,
    M_UNCOMP_COPY,
76 77
};

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
78
static void put_msb_32(aec_streamp strm, int64_t data)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
79 80 81 82 83 84 85 86 87
{
    *strm->next_out++ = data >> 24;
    *strm->next_out++ = data >> 16;
    *strm->next_out++ = data >> 8;
    *strm->next_out++ = data;
    strm->avail_out -= 4;
    strm->total_out += 4;
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
88
static void put_msb_24(aec_streamp strm, int64_t data)
89 90 91 92 93 94 95 96
{
    *strm->next_out++ = data >> 16;
    *strm->next_out++ = data >> 8;
    *strm->next_out++ = data;
    strm->avail_out -= 3;
    strm->total_out += 3;
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
97
static void put_msb_16(aec_streamp strm, int64_t data)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
98 99 100 101 102 103 104
{
    *strm->next_out++ = data >> 8;
    *strm->next_out++ = data;
    strm->avail_out -= 2;
    strm->total_out += 2;
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
105
static void put_lsb_32(aec_streamp strm, int64_t data)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
106 107 108 109 110 111 112 113 114
{
    *strm->next_out++ = data;
    *strm->next_out++ = data >> 8;
    *strm->next_out++ = data >> 16;
    *strm->next_out++ = data >> 24;
    strm->avail_out -= 4;
    strm->total_out += 4;
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
115
static void put_lsb_24(aec_streamp strm, int64_t data)
116 117 118 119 120 121 122 123
{
    *strm->next_out++ = data;
    *strm->next_out++ = data >> 8;
    *strm->next_out++ = data >> 16;
    strm->avail_out -= 3;
    strm->total_out += 3;
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
124
static void put_lsb_16(aec_streamp strm, int64_t data)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
125 126 127 128 129 130
{
    *strm->next_out++ = data;
    *strm->next_out++ = data >> 8;
    strm->avail_out -= 2;
    strm->total_out += 2;
}
131

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
132
static void put_8(aec_streamp strm, int64_t data)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
133 134 135 136 137
{
    *strm->next_out++ = data;
    strm->avail_out--;
    strm->total_out++;
}
138

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
139
static inline void u_put(aec_streamp strm, int64_t sample)
140
{
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
141 142
    int64_t x, d, th, D, lower;
    decode_state *state = strm->state;
143

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
144
    if (state->pp && (state->samples_out % state->ref_int != 0))
145 146 147
    {
        d = sample;
        x = state->last_out;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
148 149
        lower = x - state->xmin;
        th = MIN(lower, state->xmax - x);
150

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
151
        if (d <= 2 * th)
152 153 154 155 156 157
        {
            if (d & 1)
                D = - (d + 1) / 2;
            else
                D = d / 2;
        } else {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
158
            if (th == lower)
159 160 161 162 163 164
                D = d - th;
            else
                D = th - d;
        }
        sample = x + D;
    }
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
165 166
    else
    {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
167
        if (strm->flags & AEC_DATA_SIGNED)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
168 169 170 171 172 173
        {
            int m = 64 - strm->bit_per_sample;
            /* Reference samples have to be sign extended */
            sample = (sample << m) >> m;
        }
    }
174 175
    state->last_out = sample;
    state->put_sample(strm, sample);
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
176
    state->samples_out++;
177 178
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
179
static inline int64_t u_get(aec_streamp strm, unsigned int n)
180 181 182 183 184 185 186 187 188 189 190 191 192 193
{
    /**
       Unsafe get n bit from input stream

       No checking whatsoever. Read bits are dumped.
     */

    decode_state *state;

    state = strm->state;
    while (state->bitp < n)
    {
        strm->avail_in--;
        strm->total_in++;
194
        state->acc = (state->acc << 8) | *strm->next_in++;
195 196 197 198 199 200
        state->bitp += 8;
    }
    state->bitp -= n;
    return (state->acc >> state->bitp) & ((1ULL << n) - 1);
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
201
static inline int64_t u_get_fs(aec_streamp strm)
202 203 204 205 206 207 208 209
{
    /**
       Interpret a Fundamental Sequence from the input buffer.

       Essentially counts the number of 0 bits until a
       1 is encountered. TODO: faster version.
     */

210
    int64_t fs = 0;
211 212 213 214 215 216 217

    while(u_get(strm, 1) == 0)
        fs++;

    return fs;
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
218
static inline void fast_split(aec_streamp strm)
219
{
220
    int i, k;
221 222 223 224 225
    decode_state *state;

    state = strm->state;
    k = state->id - 1;

226
    if (state->ref)
227
        u_put(strm, u_get(strm, strm->bit_per_sample));
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
228

229
    for (i = state->ref; i < strm->block_size; i++)
230 231
        state->block[i] = u_get_fs(strm) << k;

232
    for (i = state->ref; i < strm->block_size; i++)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
233
    {
234 235 236 237 238
        state->block[i] += u_get(strm, k);
        u_put(strm, state->block[i]);
    }
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
239
static inline void fast_zero(aec_streamp strm)
240 241 242 243 244 245 246
{
    int i = strm->state->i;

    while (i--)
        u_put(strm, 0);
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
247
static inline void fast_se(aec_streamp strm)
248 249
{
    int i;
250
    int64_t gamma, beta, ms, delta1;
251

252
    i = strm->state->ref;
253

254
    while (i < strm->block_size)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
255
    {
256 257 258 259
        gamma = u_get_fs(strm);
        beta = second_extension[gamma][0];
        ms = second_extension[gamma][1];
        delta1 = gamma - ms;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
260

261 262 263 264 265 266 267 268 269 270
        if ((i & 1) == 0)
        {
            u_put(strm, beta - delta1);
            i++;
        }
        u_put(strm, delta1);
        i++;
    }
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
271
static inline void fast_uncomp(aec_streamp strm)
272 273 274 275 276
{
    int i;

    for (i = 0; i < strm->block_size; i++)
        u_put(strm, u_get(strm, strm->bit_per_sample));
277 278
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
279
int aec_decode_init(aec_streamp strm)
280
{
281 282 283 284 285 286
    int i, modi;
    decode_state *state;

    /* Some sanity checks */
    if (strm->bit_per_sample > 32 || strm->bit_per_sample == 0)
    {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
287
        return AEC_CONF_ERROR;
288 289 290 291 292 293
    }

    /* Internal state for decoder */
    state = (decode_state *) malloc(sizeof(decode_state));
    if (state == NULL)
    {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
294
        return AEC_MEM_ERROR;
295 296 297
    }
    strm->state = state;

298 299
    if (strm->bit_per_sample > 16)
    {
300
        state->id_len = 5;
301

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
302
        if (strm->bit_per_sample <= 24 && strm->flags & AEC_DATA_3BYTE)
303 304
        {
            state->byte_per_sample = 3;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
305
            if (strm->flags & AEC_DATA_MSB)
306 307 308 309
                state->put_sample = put_msb_24;
            else
                state->put_sample = put_lsb_24;
        }
310
        else
311 312
        {
            state->byte_per_sample = 4;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
313
            if (strm->flags & AEC_DATA_MSB)
314 315 316 317 318 319
                state->put_sample = put_msb_32;
            else
                state->put_sample = put_lsb_32;
        }
        state->out_blklen = strm->block_size
            * state->byte_per_sample;
320 321 322
    }
    else if (strm->bit_per_sample > 8)
    {
323
        state->byte_per_sample = 2;
324
        state->id_len = 4;
325
        state->out_blklen = strm->block_size * 2;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
326
        if (strm->flags & AEC_DATA_MSB)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
327
            state->put_sample = put_msb_16;
328
        else
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
329
            state->put_sample = put_lsb_16;
330
    }
331
    else
332
    {
333
        state->byte_per_sample = 1;
334
        state->id_len = 3;
335
        state->out_blklen = strm->block_size;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
336
        state->put_sample = put_8;
337 338
    }

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
339
    if (strm->flags & AEC_DATA_SIGNED)
340 341 342 343 344 345 346 347 348
    {
        state->xmin = -(1ULL << (strm->bit_per_sample - 1));
        state->xmax = (1ULL << (strm->bit_per_sample - 1)) - 1;
    }
    else
    {
        state->xmin = 0;
        state->xmax = (1ULL << strm->bit_per_sample) - 1;
    }
349

350
    state->ref_int = strm->block_size * strm->rsi;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
351
    state->in_blklen = (strm->block_size * strm->bit_per_sample
352 353 354 355 356 357
                        + state->id_len) / 8 + 1;

    modi = 1UL << state->id_len;
    state->id_table = (int *)malloc(modi * sizeof(int));
    if (state->id_table == NULL)
    {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
358
        return AEC_MEM_ERROR;
359 360 361 362 363 364 365 366
    }
    state->id_table[0] = M_LOW_ENTROPY;
    for (i = 1; i < modi - 1; i++)
    {
        state->id_table[i] = M_SPLIT;
    }
    state->id_table[modi - 1] = M_UNCOMP;

367
    state->block = (int64_t *)malloc(strm->block_size * sizeof(int64_t));
368 369
    if (state->block == NULL)
    {
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
370
        return AEC_MEM_ERROR;
371 372 373 374
    }
    strm->total_in = 0;
    strm->total_out = 0;

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
375
    state->samples_out = 0;
376
    state->bitp = 0;
377
    state->fs = 0;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
378
    state->pp = strm->flags & AEC_DATA_PREPROCESS;
379
    state->mode = M_ID;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
380
    return AEC_OK;
381 382
}

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
383
int aec_decode_end(aec_streamp strm)
384 385 386 387 388 389 390
{
    decode_state *state;

    state = strm->state;
    free(state->block);
    free(state->id_table);
    free(state);
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
391
    return AEC_OK;
392 393
}

394 395 396 397 398 399 400 401
#define ASK(n)                                           \
    do {                                                 \
        while (state->bitp < (unsigned)(n))              \
        {                                                \
            if (strm->avail_in == 0) goto req_buffer;    \
            strm->avail_in--;                            \
            strm->total_in++;                            \
            state->acc <<= 8;                            \
402
            state->acc |= *strm->next_in++;              \
403 404
            state->bitp += 8;                            \
        }                                                \
405
    } while (0)
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
406

407 408 409 410 411
#define GET(n)                                                  \
    ((state->acc >> (state->bitp - (n))) & ((1ULL << (n)) - 1))

#define DROP(n) state->bitp -= (unsigned)(n)

412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
#define ASKFS()                                                 \
    do {                                                        \
        ASK(1);                                                 \
        while ((state->acc & (1ULL << (state->bitp - 1))) == 0) \
        {                                                       \
            if (state->bitp == 1)                               \
            {                                                   \
                if (strm->avail_in == 0) goto req_buffer;       \
                strm->avail_in--;                               \
                strm->total_in++;                               \
                state->acc <<= 8;                               \
                state->acc |= *strm->next_in++;                 \
                state->bitp += 8;                               \
            }                                                   \
            state->fs++;                                        \
            state->bitp--;                                      \
        }                                                       \
    } while (0)
430 431 432

#define GETFS() state->fs

433 434 435 436 437 438 439
#define DROPFS()                                \
    do {                                        \
        state->fs = 0;                          \
        /* Needs to be here for                 \
           ASK/GET/PUT/DROP interleaving. */    \
        state->bitp--;                          \
    } while (0)
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454

#define PUT(sample)                                \
    do {                                           \
        if (strm->avail_out == 0) goto req_buffer; \
        u_put(strm, (sample));                     \
    } while (0)

#define COPYSAMPLE()                    \
    do {                                \
        ASK(strm->bit_per_sample);      \
        PUT(GET(strm->bit_per_sample)); \
        DROP(strm->bit_per_sample);     \
    } while (0)


Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
455
int aec_decode(aec_streamp strm, int flush)
456
{
457 458 459 460 461 462 463 464 465
    /**
       Finite-state machine implementation of the adaptive entropy
       decoder.

       Can work with one byte input und one sample output buffers. If
       enough buffer space is available, then faster implementations
       of the states are called. Inspired by zlib.
    */

Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
466
    int zero_blocks;
467
    int64_t gamma, beta, ms, delta1;
468 469 470 471 472 473 474 475 476 477
    int k;
    decode_state *state;

    state = strm->state;

    for (;;)
    {
        switch(state->mode)
        {
        case M_ID:
478
            if (state->pp
479
                && (state->samples_out / strm->block_size) % strm->rsi == 0)
480 481 482 483 484 485 486
                state->ref = 1;
            else
                state->ref = 0;

            ASK(state->id_len);
            state->id = GET(state->id_len);
            DROP(state->id_len);
487 488 489 490 491 492 493 494 495 496 497
            state->mode = state->id_table[state->id];
            break;

        case M_SPLIT:
            if (SAFE)
            {
                fast_split(strm);
                state->mode = M_ID;
                break;
            }

498
            if (state->ref)
499 500 501 502 503 504 505 506 507
            {
                COPYSAMPLE();
                state->n = strm->block_size - 1;
            }
            else
            {
                state->n = strm->block_size;
            }

508
            state->i = state->n - 1;
509 510 511 512 513 514
            state->mode = M_SPLIT_FS;

        case M_SPLIT_FS:
            do
            {
                ASKFS();
515
                state->block[state->i] = GETFS();
516 517
                DROPFS();
            }
518
            while(state->i--);
519

520
            state->i = state->n - 1;
521
            state->mode = M_SPLIT_OUTPUT;
522

523
        case M_SPLIT_OUTPUT:
524 525 526 527
            k = state->id - 1;
            do
            {
                ASK(k);
528
                PUT((state->block[state->i] << k) + GET(k));
529 530
                DROP(k);
            }
531
            while(state->i--);
532 533 534 535 536 537 538 539 540 541 542

            state->mode = M_ID;
            break;

        case M_LOW_ENTROPY:
            ASK(1);
            state->id = GET(1);
            DROP(1);
            state->mode = M_LOW_ENTROPY_REF;

        case M_LOW_ENTROPY_REF:
543
            if (state->ref)
544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
                COPYSAMPLE();

            if(state->id == 1)
            {
                state->mode = M_SE;
                break;
            }

            state->mode = M_ZERO_BLOCK;

        case M_ZERO_BLOCK:
            ASKFS();
            zero_blocks = GETFS() + 1;
            DROPFS();

            if (zero_blocks == ROS)
            {
561
                zero_blocks =  64 - (
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
562
                    (state->samples_out / strm->block_size)
563
                    % strm->rsi % 64);
564
            }
565 566 567 568
            else if (zero_blocks > ROS)
            {
                zero_blocks--;
            }
569

570
            if (state->ref)
571 572 573 574
                state->i = zero_blocks * strm->block_size - 1;
            else
                state->i = zero_blocks * strm->block_size;

575
            if (strm->avail_out >= state->i * state->byte_per_sample)
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600
            {
                fast_zero(strm);
                state->mode = M_ID;
                break;
            }

            state->mode = M_ZERO_OUTPUT;

        case M_ZERO_OUTPUT:
            do
                PUT(0);
            while(--state->i);

            state->mode = M_ID;
            break;

        case M_SE:
            if (SAFE)
            {
                fast_se(strm);
                state->mode = M_ID;
                break;
            }

            state->mode = M_SE_DECODE;
601
            state->i = state->ref;
602 603

        case M_SE_DECODE:
604
            while(state->i < strm->block_size)
605 606
            {
                ASKFS();
607 608 609 610 611 612 613 614 615 616 617 618
                gamma = GETFS();
                beta = second_extension[gamma][0];
                ms = second_extension[gamma][1];
                delta1 = gamma - ms;

                if ((state->i & 1) == 0)
                {
                    PUT(beta - delta1);
                    state->i++;
                }

                PUT(delta1);
619
                state->i++;
620
                DROPFS();
621 622
            }

623
            state->mode = M_ID;
624
            break;
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
625

626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
        case M_UNCOMP:
            if (SAFE)
            {
                fast_uncomp(strm);
                state->mode = M_ID;
                break;
            }

            state->i = strm->block_size;
            state->mode = M_UNCOMP_COPY;

        case M_UNCOMP_COPY:
            do
                COPYSAMPLE();
            while(--state->i);

            state->mode = M_ID;
            break;

        default:
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
646
            return AEC_STREAM_ERROR;
647 648
        }
    }
649 650

req_buffer:
Mathis Rosenhauer's avatar
Mathis Rosenhauer committed
651
    return AEC_OK;
652
}