blob: cfb04fd6cad2656520a8a06b582f545e1f28a47c [file]
/*
* Copyright (c) 2021-2026 Symas Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of the Symas Corporation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <vector>
#include <dirent.h>
#include <dlfcn.h>
#include <err.h>
#include <fcntl.h>
#include <fenv.h>
#include <math.h> // required for fpclassify(3), not in cmath
#include <setjmp.h>
#include <signal.h>
#include <syslog.h>
#include <unistd.h>
#include <stdarg.h>
#if __has_include(<errno.h>)
# include <errno.h> // for program_invocation_short_name
#endif
#include "config.h"
#include "libgcobol-fp.h"
#include "ec.h"
#include "common-defs.h"
#include "io.h"
#include "gcobolio.h"
#include "cobol-endian.h"
#include "libgcobol.h"
#include "gfileio.h"
#include "charmaps.h"
#include "valconv.h"
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <execinfo.h>
#include "exceptl.h"
#include "stringbin.h"
/* This routine evolved from a primitive binary-to-string routine that simply
peeled digits off the bottom of an __int128 by using
value % 10 + '0';
value /= 10;
That turns out to be unnecessarily slow.
The routine implemented here splits wide values into large decimal chunks
to minimize the number of divisions. Within each chunk, it emits digits
two at a time through a table lookup. */
#if defined(__cplusplus) && __cplusplus >= 201703L
# define FALLTHROUGH [[fallthrough]]
#elif defined(__GNUC__) && __GNUC__ >= 7
# define FALLTHROUGH __attribute__((fallthrough))
#else
# define FALLTHROUGH ((void)0)
#endif
static const unsigned char digits2[100][2] =
{
{0,0},{0,1},{0,2},{0,3},{0,4},{0,5},{0,6},{0,7},{0,8},{0,9},
{1,0},{1,1},{1,2},{1,3},{1,4},{1,5},{1,6},{1,7},{1,8},{1,9},
{2,0},{2,1},{2,2},{2,3},{2,4},{2,5},{2,6},{2,7},{2,8},{2,9},
{3,0},{3,1},{3,2},{3,3},{3,4},{3,5},{3,6},{3,7},{3,8},{3,9},
{4,0},{4,1},{4,2},{4,3},{4,4},{4,5},{4,6},{4,7},{4,8},{4,9},
{5,0},{5,1},{5,2},{5,3},{5,4},{5,5},{5,6},{5,7},{5,8},{5,9},
{6,0},{6,1},{6,2},{6,3},{6,4},{6,5},{6,6},{6,7},{6,8},{6,9},
{7,0},{7,1},{7,2},{7,3},{7,4},{7,5},{7,6},{7,7},{7,8},{7,9},
{8,0},{8,1},{8,2},{8,3},{8,4},{8,5},{8,6},{8,7},{8,8},{8,9},
{9,0},{9,1},{9,2},{9,3},{9,4},{9,5},{9,6},{9,7},{9,8},{9,9}
};
template<int stride>
static inline void
uint_to_8_digits_direct( unsigned int value,
unsigned char *result,
int digits,
unsigned char zero )
{
unsigned int pair;
switch( digits )
{
case 8:
pair = value % 100;
result[6*stride] = digits2[pair][0] + zero;
result[7*stride] = digits2[pair][1] + zero;
value /= 100;
FALLTHROUGH;
case 6:
pair = value % 100;
result[4*stride] = digits2[pair][0] + zero;
result[5*stride] = digits2[pair][1] + zero;
value /= 100;
FALLTHROUGH;
case 4:
pair = value % 100;
result[2*stride] = digits2[pair][0] + zero;
result[3*stride] = digits2[pair][1] + zero;
value /= 100;
FALLTHROUGH;
case 2:
/* The caller guarantees that value fits in digits, so the final pair
is already in the range zero through 99. */
result[0] = digits2[value][0] + zero;
result[stride] = digits2[value][1] + zero;
break;
default:
__builtin_unreachable();
}
}
template<int stride>
static inline void
string_from_uint64( unsigned char *result,
int digits,
uint64_t value,
unsigned char zero )
{
if( digits & 0x01 )
{
result[(digits-1)*stride]
= static_cast<unsigned char>(value % 10 + zero);
if( digits == 1 )
{
return;
}
value /= 10;
digits -= 1;
}
/* Leave the final one-to-eight digits in value. Their value is already
known to be less than 10^digits, so neither a remainder nor a final
division is needed for that last group. */
while( digits > 8 )
{
unsigned int chunk
= static_cast<unsigned int>(value % 100000000);
uint_to_8_digits_direct<stride>(
chunk,
result + (digits-8)*stride,
8,
zero);
value /= 100000000;
digits -= 8;
}
if( digits )
{
uint_to_8_digits_direct<stride>(
static_cast<unsigned int>(value),
result,
digits,
zero);
}
}
template<int stride>
static bool
binary_to_string( char *result,
int digits,
__int128 signed_value,
unsigned char zero )
{
unsigned __int128 value
= static_cast<unsigned __int128>(signed_value);
if( signed_value < 0 )
{
/* Unsigned negation also handles the minimum signed __int128 value. */
value = -value;
}
bool overflow = false;
if( digits < 39 )
{
unsigned __int128 mask
= static_cast<unsigned __int128>(__gg__power_of_ten(digits));
overflow = value >= mask;
/* Overflow should be uncommon. Avoid 128-bit division entirely when
the value already fits the requested number of digits. */
if( overflow )
{
value %= mask;
}
}
/* 10^19 is the largest power of ten that fits in uint64_t. For values
wider than 64 bits, one 128-bit division produces two pieces that can be
formatted using only 64-bit and 32-bit arithmetic. */
static const uint64_t ten_to_19 = 10000000000000000000ULL;
unsigned char *output = reinterpret_cast<unsigned char *>(result);
if( (value >> 64) == 0 )
{
string_from_uint64<stride>(
output,
digits,
static_cast<uint64_t>(value),
zero);
}
else
{
uint64_t low
= static_cast<uint64_t>(value % ten_to_19);
uint64_t high
= static_cast<uint64_t>(value / ten_to_19);
string_from_uint64<stride>(output, digits-19, high, zero);
string_from_uint64<stride>(
output+(digits-19)*stride,
19,
low,
zero);
}
return overflow;
}
extern "C"
bool
__gg__binary_to_string_ascii( char *result,
int digits,
__int128 value )
{
return binary_to_string<1>(result, digits, value, ascii_zero);
}
extern "C"
bool
__gg__binary_to_string_ebcdic( char *result,
int digits,
__int128 value )
{
return binary_to_string<1>(result, digits, value, ebcdic_zero);
}
bool
__gg__binary_to_string_encoded( char *result,
size_t digits,
__int128 value,
cbl_encoding_t encoding )
{
const charmap_t *charmap = __gg__get_charmap(encoding);
int stride = charmap->stride();
unsigned char zero
= charmap->is_like_ebcdic() ? ebcdic_zero : ascii_0;
if( stride == 1 )
{
return binary_to_string<1>(
result,
static_cast<int>(digits),
value,
zero);
}
/* Clear the complete destination once, then write each digit directly into
the byte selected by the encoding's byte order. */
size_t output_size = digits * static_cast<size_t>(stride);
memset(result, 0, output_size);
size_t digit_offset
= charmap->is_big_endian() ? static_cast<size_t>(stride-1) : 0;
char *digit_result = result + digit_offset;
if( stride == 2 )
{
return binary_to_string<2>(
digit_result,
static_cast<int>(digits),
value,
zero);
}
return binary_to_string<4>(
digit_result,
static_cast<int>(digits),
value,
zero);
}
static const unsigned char bin2pd[100] =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
};
static inline void
uint32_to_packed( unsigned char *result,
int bytes,
uint32_t value )
{
unsigned int pair;
switch( bytes )
{
case 4:
pair = value % 100;
result[3] = bin2pd[pair];
value /= 100;
FALLTHROUGH;
case 3:
pair = value % 100;
result[2] = bin2pd[pair];
value /= 100;
FALLTHROUGH;
case 2:
pair = value % 100;
result[1] = bin2pd[pair];
value /= 100;
FALLTHROUGH;
case 1:
/* Retain the low-order pair when the source has more decimal digits than
the destination. */
result[0] = bin2pd[value % 100];
break;
default:
__builtin_unreachable();
}
}
static inline void
uint64_to_packed( unsigned char *result,
int bytes,
uint64_t value )
{
/* Four packed bytes hold eight decimal digits. Extracting four bytes at a
time limits the divisions of value to one per four output bytes; the
remaining divisions operate on 32-bit chunks. */
while( bytes > 4 )
{
uint32_t chunk
= static_cast<uint32_t>(value % 100000000);
uint32_to_packed(result+bytes-4, 4, chunk);
value /= 100000000;
bytes -= 4;
}
if( bytes )
{
uint32_t final_chunk;
if( value >> 32 )
{
/* Truncate in base ten before narrowing to uint32_t. */
final_chunk = static_cast<uint32_t>(value % 100000000);
}
else
{
final_chunk = static_cast<uint32_t>(value);
}
uint32_to_packed(
result,
bytes,
final_chunk);
}
}
static inline void
moderate_uint128_to_packed( unsigned char *result,
int bytes,
unsigned __int128 value )
{
unsigned char *d = result + bytes;
/* GCC expands division of an unsigned __int128 by the constant 100 inline.
For moderately wide values, peeling a few pairs this way is faster than
invoking the general 128-bit division helper for a large divisor. */
while( d > result && value >> 64 )
{
*(--d) = bin2pd[static_cast<unsigned int>(value % 100)];
value /= 100;
}
uint64_to_packed(
result,
static_cast<int>(d-result),
static_cast<uint64_t>(value));
}
static inline void
large_uint128_to_packed( unsigned char *result,
int bytes,
unsigned __int128 value )
{
static const uint64_t ten_to_18 = 1000000000000000000ULL;
static const uint64_t ten_to_19 = 10000000000000000000ULL;
/* One division at 10^19 divides every supported packed value into two
uint64_t values. Since 19 is odd, one packed byte crosses the boundary
between the two values. */
uint64_t low
= static_cast<uint64_t>(value % ten_to_19);
uint64_t high
= static_cast<uint64_t>(value / ten_to_19);
/* low contains the low-order 19 decimal digits. It therefore contains all
the digits needed by a destination of nine bytes or fewer. */
if( bytes <= 9 )
{
uint64_to_packed(result, bytes, low);
return;
}
unsigned int low_leading_digit
= static_cast<unsigned int>(low / ten_to_18);
uint64_t low_trailing_digits = low % ten_to_18;
unsigned int high_trailing_digit
= static_cast<unsigned int>(high % 10);
high /= 10;
uint64_to_packed(result+bytes-9, 9, low_trailing_digits);
result[bytes-10]
= bin2pd[high_trailing_digit*10 + low_leading_digit];
uint64_to_packed(result, bytes-10, high);
}
extern "C"
void
__gg__binary_to_packed( unsigned char *result,
int digits,
__int128 value )
{
/* The caller supplies a positive value. For a signable item, it has
already multiplied the magnitude by ten to reserve the low nybble for
the sign. */
unsigned __int128 magnitude
= static_cast<unsigned __int128>(value);
int bytes = (digits+1)/2;
if( (magnitude >> 64) == 0 )
{
uint64_to_packed(result, bytes, static_cast<uint64_t>(magnitude));
}
else
{
/* This threshold is a performance choice, not a numeric boundary. Below
10^27, peeling a small number of pairs is faster on current GCC targets.
At and above it, the single 10^19 split is faster. */
static const unsigned __int128 ten_to_27
= static_cast<unsigned __int128>(10000000000000ULL)
* 100000000000000ULL;
if( magnitude < ten_to_27 )
{
moderate_uint128_to_packed(result, bytes, magnitude);
}
else
{
large_uint128_to_packed(result, bytes, magnitude);
}
}
}
const unsigned char __gg__dp2bin[256] =
{
// This table is used both by the compile-time and the run-time. Given the
// packed decimal byte 0x23, it provides the equivalent decimal value of
// 23. This table is not used on the final byte of COMP-3 values; that
// digit has to be extracted specifically.
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
//--------------------------------------------------------------
00, 01, 02, 03, 04, 05, 06, 07, 8, 9, 0, 0, 0, 0, 0, 0, // 0x00
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, // 0x10
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, // 0x20
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, // 0x30
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0, 0, 0, 0, 0, 0, // 0x40
50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 0, 0, 0, 0, 0, 0, // 0x50
60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 0, 0, 0, 0, 0, 0, // 0x60
70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 0, 0, 0, 0, 0, 0, // 0x70
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 0, 0, 0, 0, 0, 0, // 0x80
90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 0, 0, 0, 0, 0, 0, // 0x90
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xB0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xF0
};
static inline uint32_t
four_packed_bytes_to_uint32(const unsigned char *p)
{
return static_cast<uint32_t>(__gg__dp2bin[p[0]]) * 1000000
+ static_cast<uint32_t>(__gg__dp2bin[p[1]]) * 10000
+ static_cast<uint32_t>(__gg__dp2bin[p[2]]) * 100
+ static_cast<uint32_t>(__gg__dp2bin[p[3]]);
}
static inline uint32_t
initial_packed_bytes_to_uint32( const unsigned char *p,
int nplaces )
{
switch( nplaces )
{
case 1:
return __gg__dp2bin[p[0]];
case 2:
return __gg__dp2bin[p[0]] * 100
+ __gg__dp2bin[p[1]];
case 3:
return __gg__dp2bin[p[0]] * 10000
+ __gg__dp2bin[p[1]] * 100
+ __gg__dp2bin[p[2]];
case 4:
return four_packed_bytes_to_uint32(p);
default:
__builtin_unreachable();
}
}
static inline uint64_t
packed_bytes_to_uint64( const unsigned char *p,
int nplaces )
{
int first = nplaces & 3;
if( first == 0 )
{
first = 4;
}
uint64_t value = initial_packed_bytes_to_uint32(p, first);
p += first;
nplaces -= first;
while( nplaces )
{
value = value * 100000000 + four_packed_bytes_to_uint32(p);
p += 4;
nplaces -= 4;
}
return value;
}
static inline unsigned __int128
packed_bytes_to_uint128( const unsigned char *p,
int nplaces )
{
switch( nplaces )
{
case 0:
return 0;
case 1:
case 2:
case 3:
case 4:
return initial_packed_bytes_to_uint32(p, nplaces);
default:
break;
}
if( nplaces <= 9 )
{
return packed_bytes_to_uint64(p, nplaces);
}
/* The low nine packed bytes contain 18 decimal digits and fit in uint64_t.
Decode both halves with 64-bit arithmetic, then combine them with one
128-bit multiplication. Only a 19-byte COMP-6 value has a ten-byte high
portion; split that portion into one byte and nine bytes. */
static const uint64_t ten_to_18 = 1000000000000000000ULL;
int high_places = nplaces - 9;
unsigned __int128 high;
if( high_places <= 9 )
{
high = packed_bytes_to_uint64(p, high_places);
}
else
{
high = static_cast<unsigned __int128>(__gg__dp2bin[p[0]]) * ten_to_18
+ packed_bytes_to_uint64(p+1, 9);
}
uint64_t low = packed_bytes_to_uint64(p+high_places, 9);
return high * ten_to_18 + low;
}
extern "C"
__int128
__gg__packed_to_binary( const unsigned char *psz,
int nplaces ) // Number of bytes
{
// Check to see if the final nybble is a sign bit:
unsigned int sign = psz[nplaces-1] & 0x0F;
bool signable = sign >= 0x0A;
unsigned __int128 magnitude;
if( signable )
{
magnitude = packed_bytes_to_uint128(psz, nplaces-1) * 10
+ (psz[nplaces-1] >> 4);
}
else
{
magnitude = packed_bytes_to_uint128(psz, nplaces);
}
if( sign == 0x0B || sign == 0x0D )
{
return -static_cast<__int128>(magnitude);
}
return static_cast<__int128>(magnitude);
}