[FFmpeg-devel] [PATCH V2] [RFC] GSoC: FLIF16 Image format parser
Anamitra Ghorui
aghorui at teknik.io
Mon Mar 2 12:51:00 EET 2020
Hello,
> Is there an upper bound for the size of these integers? In my
> experience, varints in multimedia code are used to save a few bits in
> the file than to allow very large integers. You could make the code much
> simpler if you had an upper bound.
>
> Also, there is probably already similar code, because it is a common
> feature.
According to the specification of the file format, there is no mention of an
upper bound for the integer: https://flif.info/spec.html#_part_1_main_header
>> + * magnitude of the varint, similar to UTF-8 encoding.
>
> This is not how UTF-8 works.
I was talking about it in the context of the 'prefix coding' it uses. UTF-8
uses the MSB bits to indicate how many successive bits aside from the starting
bit are included in a character. The varint does a similar thing in the manner
that it also uses an MSB bit to indicate that a successive bit is included in
the number. Hovever aside from this superficial similarity there is not much
in common.
>> +varint_t *flif16_varint_alloc(unsigned int pad, uint8_t lsbval)
>> +{
>> + varint_t *v = av_mallocz(sizeof(varint_t));
>
>When possible, prefer sizeof(*var) to sizeof(type).
Okay
>> + if(!v)
>
> Our coding style mandates spaces after if, for, etc.
Yes. There are several other non-uniformities in the code.
>> + return v;
>
> return NULL is more idiomatic.
True. I was looking through the AVpacket functions and in the av_packet_alloc
function: https://ffmpeg.org/doxygen/trunk/avpacket_8c_source.html#l00051 (line 51)
there's a similar pattern followed:
AVPacket *pkt = av_mallocz(sizeof(AVPacket));
if (!pkt)
return pkt;
Is there a special reason for this?
>> + v->buf = av_mallocz(sizeof(uint8_t) * (pad + 1));
>
>sizeof(uint8_t) is guaranteed to be 1 on supported platforms.
>
>Missing malloc check. Better make it:
>
> v = av_mallocz(sizeof(*v) + pad);
>
>to allocate both at the same time: more efficient.
I see. the stucture should therefore be defined with buf after buf_size
(hence putting pad + 1 bytes in contiguous storage but there may be a problem
with the byte padding of the struct), or should buf point to v + sizeof(*v)?
(* See follow-up content below)
>> + v->buf[pad] = lsbval & 127;
>> + v->buf_size = pad + 1;
>> + return v;
>> +}
>> +
>> +void flif16_varint_free(varint_t *v)
>> +{
>> + av_free(v->buf);
>> + av_free(v);
>> +}
>> +
>> +// May be made faster by providing MSByte as a parameter.
>> +unsigned int flif16_varint_inc(varint_t *v)
>> +{
>> + unsigned int msb;
>
>> + av_assert0(v->buf_size);
>
>If speed is a concern, use av_assert1().
Okay
>> + msb = v->buf_size - 1;
>> +
>> + for(; msb > 0; --msb) {
>
>> + if(v->buf[msb] == 127) {
>> + v->buf[msb] = 0;
>> + } else {
>> + ++v->buf[msb];
>> + return msb;
>> + }
>
>Did you test this code for varint { 127, 127, 127 }?
I have tested it separately. In the given code, the function will set the value
to { 128, 0, 0 }, which is an incorrect representation. But for the puropses of
the parser and the decoder where we are only concerned with bound checking with
an existing number, such a situation will never occur. However I can see that
there will be issues when the encoder will be written since then we will have
to make the varint and write the varint to a file.
>> + }
>> +
>> + ++v->buf[msb];
>> + return msb;
>> +}
>> +
>>
>> +void flif16_varint_append(varint_t *v, uint8_t vs)
>> +{
>> + av_fast_realloc(v->buf, &v->buf_size, v->buf_size + 1);
>
>> + av_assert0(v->buf);
>
>An assert cannot be used to check for a memory allocation failure.
I think I have to use av_log then.
>> + v->buf[v->buf_size - 1] = vs & 127;
>> +}
>
> Increasing a buffer one by one is inefficient. Please consider doubling
> the size.
Should I double the size everytime v->buf_size becomes
equal to the actual number of byte-segments of the varint?
>> +typedef struct varint_t {
>> + uint8_t *buf;
>
> To avoid the indirection and extra malloc, you can make it
> uint8_t buf[1] at the end of the structure.
If I add it to the end of the struct, the compiler will likely add padding
bits to it, but I don't think its a problem since the padding bits will be set
to zero anyway and therefore can be used in the buffer.
>> + unsigned int buf_size; // Assuming that no one will try to make an image of
>> + // dimensions greater than 2^32x2^32
>
> unsigned is enough. Same everywhere.
>
> I suppose size_t would be way overkill for this case.
Ok
>> +} varint_t;
>
> Names ending in _t are reserved by the C standard. FFmpeg uses types
> with capitals and FF prefix for private APIs.
FLIF16Varint should be fine then?
>> +
>> +/**
>> + * Allocates an empty varint.
>> + * Note the 'pad' parameter here. This can be used to pad the number with
>> + * preceeding bytes if we already know the number of bytes in the number.
>> + * @param pad The number of chars to pad the varint with.
>> + * @param lsbval The value to put in the least significant BYTE
>> + * @return The pointer to the allocated varint.
>> + */
>
>> +varint_t *flif16_varint_alloc(unsigned int pad, uint8_t lsbval);
>
> All global functions are required to have the ff_ prefix (unless they
> are public, then avsomething_).
Okay
>> +/**
>> + * Increments a varint.
>> + * @param v The varint to increment.
>> + * @return The position of the most significant BYTE starting from 0.This
>> + * can be used to avoid using the comparison function.
>> + */
>> +unsigned int flif16_varint_inc(varint_t *v);
>
> Please document the API constraint that the buffer must need to be large
> enough.
Okay
>> +typedef enum FLIF16ParseStates {
>> + FLIF16_HEADER = 1,
>> + FLIF16_METADATA,
>> + FLIF16_BITSTREAM,
>> + // FLIF16_TRANSFORM,
>> + FLIF16_PIXELDATA,
>> + FLIF16_CHECKSUM,
>> + FLIF16_VARINT
>> +} flif16_states;
>
> We usually name the enum and the corresponding types the same way.
I had modelled most of the code after the GIF parser, and it names the enum
values in the same manner:
https://ffmpeg.org/doxygen/trunk/gif__parser_8c_source.html
I will probably prefix them with 'FLIF16_STATE_' then.
>> + } else if(f->index == 4) {
>> + // Handle the size varint
>> + vcur = flif16_varint_alloc(0, buf[index]);
>
>> + av_assert0(vcur);
>
> Same as above, and same below: av_assert0() can only be used for
> conditions that can be proven statically.
Ok
>> +static int flif16_parse(AVCodecParserContext *s, AVCodecContext *avctx,
>> + const uint8_t **poutbuf, int *poutbuf_size,
>> + const uint8_t *buf, int buf_size)
>
> Alignment is off.
I'll fix it (If you were talking about the textual alignment).
>
> Regards,
>
> --
> Nicolas George
Thanks.
More information about the ffmpeg-devel
mailing list