Skip to content

Commit

Permalink
bin/base64: decode: ignore newlines
Browse files Browse the repository at this point in the history
By popular demand, ignore newlines in the encoded input. This achieves
bug compatibility with GNU base64.

The algorithm is quite naive and slow, checking each byte independently.
It can definitely be improved by vectorizing it.
  • Loading branch information
aklomp committed Jan 9, 2024
1 parent 6756832 commit da26042
Showing 1 changed file with 61 additions and 11 deletions.
72 changes: 61 additions & 11 deletions bin/base64.c
Expand Up @@ -340,7 +340,21 @@ encode (const struct config *config, struct buffer *buf)
return true;
}

static int
static inline const char *
find_newline (const char *p, size_t len)
{
// This is very naive and can definitely be improved.
while (len-- > 0) {
if (*p == '\n') {
return p;
}
p++;
}

return NULL;
}

static bool
decode (const struct config *config, struct buffer *buf)
{
size_t nread, nout;
Expand All @@ -352,18 +366,54 @@ decode (const struct config *config, struct buffer *buf)
// Read encoded data into the buffer. Use the smallest buffer size to
// be on the safe side: the decoded output will fit the raw buffer.
while ((nread = fread(buf->enc, 1, BUFFER_RAW_SIZE, config->fp)) > 0) {
char *s = buf->enc;
size_t avail = nread;

// By popular demand, this utility tries to be bug-compatible
// with GNU `base64'. That includes silently ignoring newlines
// in the input. Tokenize the input on newline characters.
// This is quite slow, and at some point we will want to
// vectorize this.
while (avail > 0) {

// Find the next newline character.
const char *p = find_newline(s, avail);

// Find the length of the next chunk. If no newline is
// found, use the rest of the buffer.
size_t len = p == NULL ? avail : (size_t) (p - s);

// Ignore empty chunks.
if (len == 0) {
s++;
avail--;
continue;
}

// Decode the input into the raw buffer.
if (base64_stream_decode(&state, buf->enc, nread,
buf->raw, &nout) == 0) {
fprintf(stderr, "%s: %s: decoding error\n",
config->name, config->file);
return false;
}
// Decode the input into the raw buffer.
if (base64_stream_decode(&state, s, len,
buf->raw, &nout) == 0) {
fprintf(stderr, "%s: %s: decoding error\n",
config->name, config->file);
return false;
}

// Append the raw data to the output stream.
if (write_stdout(config, buf->raw, nout) == false) {
return false;
// Append the raw data to the output stream.
if (write_stdout(config, buf->raw, nout) == false) {
return false;
}

// Bail out if the whole string has been consumed.
if (p == NULL) {
break;
}

// Add the newline to the chunk length.
len++;

// Move the start pointer and length past the chunk.
s += len;
avail -= len;
}
}

Expand Down

0 comments on commit da26042

Please sign in to comment.