newlib-cygwin/newlib/libc/stdlib/a64l.c

193 lines
3.7 KiB
C

/*
FUNCTION
<<a64l>>,<<l64a>>---convert between radix-64 ascii string and long
INDEX
a64l
INDEX
l64a
ANSI_SYNOPSIS
#include <stdlib.h>
long a64l(const char *<[input]>);
char *l64a(long <[input]>);
TRAD_SYNOPSIS
#include <stdlib.h>
long a64l(<[input]>)
const char *<[input]>;
char *l64a(<[input]>)
long <[input]>;
DESCRIPTION
Conversion is performed between long and radix-64 characters. The <<l64a>> routine
transforms up to 32-bits of input value starting from least significant bits to
the most significant bits. The input value is split up into a maximum of 5
groups of 6-bits and possibly one group of 2 bits (bits 31 and 30).
Each group of 6 bits forms a value from 0-63 which is translated into a character
as follows:
0 = '.'
1 = '/'
2-11 = '0' to '9'
12-37 = 'A' to 'Z'
38-63 = 'a' to 'z'
When remaining bits are zero or all bits have been translated, a nul terminator
is appended to the string. An input value of 0 results in the empty string.
The <<a64l>> performs the reverse translation. Each character is used to generate
a 6-bit value for up to 30 bits and then a 2-bit value to complete a 32-bit result.
The nul terminator means that the remaining digits are 0. An empty input string or
NULL string results in 0L. An invalid string results in undefined behavior.
If the size of a long is > 32 bits, the result is sign-extended.
RETURNS
<<l64a>> returns a nul-terminated string of 0 to 6 characters.
<<a64l>> returns the 32-bit translated value from the input character string.
PORTABILITY
<<l64a>> and <<a64l>> are non-ANSI and are defined by the Single Unix Specification.
Supporting OS subroutines required: None.
*/
#include <_ansi.h>
#include <stdlib.h>
#include <limits.h>
long
_DEFUN (a64l, (input),
const char *input)
{
char *ptr;
char ch;
int i, digit;
unsigned long result = 0;
if (input == NULL)
return 0;
ptr = input;
/* it easiest to go from most significant digit to least so find end of input or up
to 6 characters worth */
for (i = 0; i < 6; ++i)
{
if (*ptr)
++ptr;
}
while (ptr > input)
{
ch = *(--ptr);
#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
if (ch >= 'a')
digit = (ch - 'a') + 38;
else if (ch >= 'A')
digit = (ch - 'A') + 12;
else if (ch >= '0')
digit = (ch - '0') + 2;
else if (ch == '/')
digit = 1;
else
digit = 0;
#else /* !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) */
switch (ch)
{
case '/':
digit = 1;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
digit = (ch - '0') + 2;
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'H':
case 'I':
case 'J':
case 'K':
case 'L':
case 'M':
case 'N':
case 'O':
case 'P':
case 'Q':
case 'R':
case 'S':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z':
digit = (ch - 'A') + 12;
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
digit = (ch - 'A') + 38;
break;
default:
digit = 0;
break;
}
#endif /* !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) */
result = (result << 6) + digit;
}
#if LONG_MAX > 2147483647
/* for implementations where long is > 32 bits, the result must be sign-extended */
if (result & 0x80000000)
return (((long)-1 >> 32) << 32) + result;
#endif
return result;
}