Fwd: Sample code: stored deflate blocks
Cosmin Truta <[email protected]>
| Newsgroups | gmane.comp.graphics.png.general |
|---|---|
| Message-ID | <CAAoVtZxLTBgp1WOzbE6mr_F9sxr4ESSe1kTzDEWd9niVjASOSw@mail.gmail.com> |
Hi,
Regardless whether the eXIf specification ends up with or without
deflate encoding, I wrote some sample code that can be used to store a
metadata block in uncompressed form. For example, this code can be
used for iCCP, as well as any future chunk data that is not required
to be compressed.
This code (or a variation of it) may go in a non-normative Annex in a
future version of the PNG spec.
The code was written to be as simple to use as possible. The user
application requires one macro calculation (to get the encoded stream
size) and one function call (to store the encoded stream in a memory
block). The function is secured in a manner similar to snprintf.
I tested this code only briefly, encoding a small buffer. It Worked For Me (tm).
***
/* Copyright (C) 2017 Cosmin Truta. */
/* This software is distributed under the zlib license. */
#define ZSTORE_STREAM_SIZE(raw_size) \
( \
/* magic header size */ 2 + \
/* leading blocks */ (raw_size / 0xffffU) * (5 + 0xffffU) + \
/* trailing block */ (raw_size % 0xffffU + 5) + \
/* Adler-32 checksum */ 4 \
)
size_t zstore(void *out_zbuf,
size_t out_zbuf_size,
const void *in_buf,
size_t in_buf_size)
{
size_t result = ZSTORE_STREAM_SIZE(in_buf_size);
if (out_zbuf_size >= result)
{
/* Initialize the magic constants. See RFC-1950. */
const char cmf = '\x08';
const char flg = '\x1d';
/* Initialize the stored block headers. See RFC-1951. */
static const char leading_block_header[5] =
{ '\x00', '\xff', '\xff', '\x00', '\x00' };
/* Initialize the trailing Adler-32 checksum. See RFC-1950. */
unsigned long checksum = adler32(1L, in_buf, in_buf_size);
/* Initialize the in/out pointers. */
char *out_ptr = (char *)out_zbuf;
const char *in_ptr = (const char *)in_buf;
size_t in_remainder = in_buf_size;
/* Write the deflate header. */
*out_ptr++ = cmf;
*out_ptr++ = flg;
/* Write the leading blocks, if any. */
while (in_remainder > 0xffffU)
{
memcpy(out_ptr, leading_block_header, 5);
memcpy(out_ptr + 5, in_ptr, 0xffffU);
out_ptr += 5 + 0xffffU;
in_ptr += 0xffffU;
in_remainder -= 0xffffU;
}
/* Write the trailing block. */
out_ptr[0] = '\x01';
out_ptr[1] = (char)(in_remainder & 0xffU);
out_ptr[2] = (char)((in_remainder >> 8) & 0xffU);
out_ptr[3] = (char)(~out_ptr[1] & 0xffU);
out_ptr[4] = (char)(~out_ptr[2] & 0xffU);
memcpy(out_ptr + 5, in_ptr, in_remainder);
out_ptr += 5 + in_remainder;
/* Write the Adler32 checksum. */
out_ptr[0] = (char)((checksum >> 24) & 0xffU);
out_ptr[1] = (char)((checksum >> 16) & 0xffU);
out_ptr[2] = (char)((checksum >> 8) & 0xffU);
out_ptr[3] = (char)(checksum & 0xffU);
}
return result;
}
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, SlashDot.org! http://sdm.link/slashdot