https://gcc.gnu.org/g:11d9f143494723200268654a252a2da108820bfe
commit r17-3957-g11d9f143494723200268654a252a2da108820bfe Author: Robert Dubner <[email protected]> Date: Sun Sep 6 13:55:40 2026 -0400 cobol: Move STRING and UNSTRING routines to unstring.cc file. This cuts down the unwieldy size of the libgcobol.cc file. libgcobol/ChangeLog: * Makefile.am: Incorporate new unstring.cc file. * Makefile.in: Regenerate. * libgcobol.cc (string_operand): Moved. (__gg__string_1): Likewise. (find_string_24): Likewise. (__gg__string_24): Likewise. (__gg__string): Likewise. (struct unstring_finder_1): Likewise. (struct unstring_finder_24): Likewise. (unstring_fixed): Likewise. (__gg__unstring_1): Likewise. (__gg__unstring_24): Likewise. (__gg__unstring): Likewise. (__gg__move_stash_release): Formatting. * libgcobol.h (__gg__move_literala): New declaration. * unstring.cc: New file. gcc/testsuite/ChangeLog: * cobol.dg/group2/EXIT_PERFORM_CYCLE.cob: Removed. * cobol.dg/group2/EXIT_PERFORM_CYCLE.out: Removed. Diff: --- .../cobol.dg/group2/EXIT_PERFORM_CYCLE.cob | 16 - .../cobol.dg/group2/EXIT_PERFORM_CYCLE.out | 1 - libgcobol/Makefile.am | 37 +- libgcobol/Makefile.in | 38 +- libgcobol/libgcobol.cc | 928 +----------------- libgcobol/libgcobol.h | 7 + libgcobol/unstring.cc | 1022 ++++++++++++++++++++ 7 files changed, 1069 insertions(+), 980 deletions(-) diff --git a/gcc/testsuite/cobol.dg/group2/EXIT_PERFORM_CYCLE.cob b/gcc/testsuite/cobol.dg/group2/EXIT_PERFORM_CYCLE.cob deleted file mode 100644 index 5e6a2152dd15..000000000000 --- a/gcc/testsuite/cobol.dg/group2/EXIT_PERFORM_CYCLE.cob +++ /dev/null @@ -1,16 +0,0 @@ - *> Do not edit this generated file. See README.txt - *> { dg-do run } - *> { dg-output-file "group2/EXIT_PERFORM_CYCLE.out" } - - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - PROCEDURE DIVISION. - PERFORM 2 TIMES - DISPLAY "OK" NO ADVANCING - END-DISPLAY - EXIT PERFORM CYCLE - DISPLAY "NOT OK" - END-DISPLAY - END-PERFORM - STOP RUN. - diff --git a/gcc/testsuite/cobol.dg/group2/EXIT_PERFORM_CYCLE.out b/gcc/testsuite/cobol.dg/group2/EXIT_PERFORM_CYCLE.out deleted file mode 100644 index d65874ef6651..000000000000 --- a/gcc/testsuite/cobol.dg/group2/EXIT_PERFORM_CYCLE.out +++ /dev/null @@ -1 +0,0 @@ -OKOK diff --git a/libgcobol/Makefile.am b/libgcobol/Makefile.am index 4efbcee119f5..18f04bad0d38 100644 --- a/libgcobol/Makefile.am +++ b/libgcobol/Makefile.am @@ -51,24 +51,25 @@ libsubincludedir = $(libsubdir) ## 2.2.12 Automatic Dependency Tracking ## Automake generates code for automatic dependency tracking by default -libgcobol_la_SOURCES = \ - charmaps.cc \ - constants.cc \ - gfileio.cc \ - gmath.cc \ - inspect.cc \ - intrinsic.cc \ - io.cc \ - libgcobol.cc \ - posix/shim/errno.cc \ - posix/shim/fstat.cc \ - posix/shim/localtime.cc \ - posix/shim/lseek.cc \ - posix/shim/open.cc \ - posix/shim/stat.cc \ - stringbin.cc \ - valconv.cc \ - xmlparse.cc +libgcobol_la_SOURCES = \ + charmaps.cc \ + constants.cc \ + gfileio.cc \ + gmath.cc \ + inspect.cc \ + intrinsic.cc \ + io.cc \ + libgcobol.cc \ + posix/shim/errno.cc \ + posix/shim/fstat.cc \ + posix/shim/localtime.cc \ + posix/shim/lseek.cc \ + posix/shim/open.cc \ + posix/shim/stat.cc \ + stringbin.cc \ + unstring.cc \ + valconv.cc \ + xmlparse.cc libgcobol_compat_gnu_la_SOURCES = \ compat/gnu/udf/cobrt-file-status.cbl \ diff --git a/libgcobol/Makefile.in b/libgcobol/Makefile.in index e1286e3ded25..67b0282834b2 100644 --- a/libgcobol/Makefile.in +++ b/libgcobol/Makefile.in @@ -188,7 +188,7 @@ am__dirstamp = $(am__leading_dot)dirstamp @BUILD_LIBGCOBOL_TRUE@ posix/shim/localtime.lo \ @BUILD_LIBGCOBOL_TRUE@ posix/shim/lseek.lo posix/shim/open.lo \ @BUILD_LIBGCOBOL_TRUE@ posix/shim/stat.lo stringbin.lo \ -@BUILD_LIBGCOBOL_TRUE@ valconv.lo xmlparse.lo +@BUILD_LIBGCOBOL_TRUE@ unstring.lo valconv.lo xmlparse.lo libgcobol_la_OBJECTS = $(am_libgcobol_la_OBJECTS) @BUILD_LIBGCOBOL_TRUE@am_libgcobol_la_rpath = -rpath $(toolexeclibdir) libgcobol_compat_gnu_la_LIBADD = @@ -476,23 +476,24 @@ gcc_version := $(shell @get_gcc_base_ver@ $(top_srcdir)/../gcc/BASE-VER) @BUILD_LIBGCOBOL_TRUE@libsubdir := $(libdir)/gcc/$(target_noncanonical)/$(version)$(MULTISUBDIR)/cobol @BUILD_LIBGCOBOL_TRUE@libsubincludedir = $(libsubdir) @BUILD_LIBGCOBOL_TRUE@libgcobol_la_SOURCES = \ -@BUILD_LIBGCOBOL_TRUE@ charmaps.cc \ -@BUILD_LIBGCOBOL_TRUE@ constants.cc \ -@BUILD_LIBGCOBOL_TRUE@ gfileio.cc \ -@BUILD_LIBGCOBOL_TRUE@ gmath.cc \ -@BUILD_LIBGCOBOL_TRUE@ inspect.cc \ -@BUILD_LIBGCOBOL_TRUE@ intrinsic.cc \ -@BUILD_LIBGCOBOL_TRUE@ io.cc \ -@BUILD_LIBGCOBOL_TRUE@ libgcobol.cc \ -@BUILD_LIBGCOBOL_TRUE@ posix/shim/errno.cc \ -@BUILD_LIBGCOBOL_TRUE@ posix/shim/fstat.cc \ -@BUILD_LIBGCOBOL_TRUE@ posix/shim/localtime.cc \ -@BUILD_LIBGCOBOL_TRUE@ posix/shim/lseek.cc \ -@BUILD_LIBGCOBOL_TRUE@ posix/shim/open.cc \ -@BUILD_LIBGCOBOL_TRUE@ posix/shim/stat.cc \ -@BUILD_LIBGCOBOL_TRUE@ stringbin.cc \ -@BUILD_LIBGCOBOL_TRUE@ valconv.cc \ -@BUILD_LIBGCOBOL_TRUE@ xmlparse.cc +@BUILD_LIBGCOBOL_TRUE@ charmaps.cc \ +@BUILD_LIBGCOBOL_TRUE@ constants.cc \ +@BUILD_LIBGCOBOL_TRUE@ gfileio.cc \ +@BUILD_LIBGCOBOL_TRUE@ gmath.cc \ +@BUILD_LIBGCOBOL_TRUE@ inspect.cc \ +@BUILD_LIBGCOBOL_TRUE@ intrinsic.cc \ +@BUILD_LIBGCOBOL_TRUE@ io.cc \ +@BUILD_LIBGCOBOL_TRUE@ libgcobol.cc \ +@BUILD_LIBGCOBOL_TRUE@ posix/shim/errno.cc \ +@BUILD_LIBGCOBOL_TRUE@ posix/shim/fstat.cc \ +@BUILD_LIBGCOBOL_TRUE@ posix/shim/localtime.cc \ +@BUILD_LIBGCOBOL_TRUE@ posix/shim/lseek.cc \ +@BUILD_LIBGCOBOL_TRUE@ posix/shim/open.cc \ +@BUILD_LIBGCOBOL_TRUE@ posix/shim/stat.cc \ +@BUILD_LIBGCOBOL_TRUE@ stringbin.cc \ +@BUILD_LIBGCOBOL_TRUE@ unstring.cc \ +@BUILD_LIBGCOBOL_TRUE@ valconv.cc \ +@BUILD_LIBGCOBOL_TRUE@ xmlparse.cc @BUILD_LIBGCOBOL_TRUE@libgcobol_compat_gnu_la_SOURCES = \ @BUILD_LIBGCOBOL_TRUE@ compat/gnu/udf/cobrt-file-status.cbl \ @@ -830,6 +831,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/io.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgcobol.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stringbin.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unstring.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/valconv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmlparse.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@posix/shim/$(DEPDIR)/errno.Plo@am__quote@ diff --git a/libgcobol/libgcobol.cc b/libgcobol/libgcobol.cc index ffe951ed81f3..445ddb5ffffc 100644 --- a/libgcobol/libgcobol.cc +++ b/libgcobol/libgcobol.cc @@ -7794,450 +7794,6 @@ normalize_for_inspect_format_4(const cblc_field_t *var, return retval; } -// The compiler ensures that every operand uses the target encoding, so an -// ordinary operand can be used directly. -static inline const char * -string_operand(const cblc_referlet_t &refer, - charmap_t *charmap, - size_t stride, - char figurative[4], - size_t *size) - { - const cblc_field_t *field = refer.field; - if( !field ) - { - *size = 0; - return figurative; - } - - cbl_figconst_t figconst = - static_cast<cbl_figconst_t>(field->attr & FIGCONST_MASK); - // We have a corner case to deal with: - if( !figconst - && field->name[0] == 'N' - && strcmp(field->name, "NULLS") == 0 ) - { - figconst = null_value_e; - } - - if( figconst ) - { - cbl_char_t figchar = '\0'; - switch( figconst ) - { - case low_value_e : - figchar = charmap->low_value_character(); - break; - case zero_value_e : - figchar = charmap->mapped_character(ascii_0); - break; - case space_value_e : - figchar = charmap->mapped_character(ascii_space); - break; - case quote_value_e : - figchar = charmap->quote_character(); - break; - case high_value_e : - figchar = charmap->high_value_character(); - break; - case null_value_e: - break; - default: - abort(); - break; - } - charmap->putch(figchar, figurative, size_t(0)); - *size = stride; - return figurative; - } - - *size = refer.size; - return as_chars(field->data + refer.offset); - } - -extern "C" -int -__gg__string_1(const size_t integers[], const cblc_referlet_t *ref) - { - // The first integer is the count of identifier-2 values. Call it N - // The following N integers are the counts of each of the identifier-1 - // values, - // one for each identifier-1. Call them M. - - // The first refer is the target - // The second refer is the pointer - // The third refer is identifier-2 for N1 - // That's followed by M1 identifier-1 values - // That's followed by identifier2 for N2 - // And so on - - static const int INDEX_OF_POINTER = 1; - - size_t index_cblc = 0 ; - - // Pick up the target - const cblc_field_t *tgt = ref[index_cblc].field; - - // Pick up the target encoding, which according to the ISO specification - // controls all the parameters. - cbl_encoding_t tgt_encoding = tgt->encoding; - charmap_t *charmap = __gg__get_charmap(tgt_encoding); - - // Pick up the rest of the parameters - size_t tgt_o = ref[index_cblc].offset; - size_t tgt_s = ref[index_cblc].size; - index_cblc += 1; - - char *dest = as_chars(tgt->data + tgt_o); - size_t dest_length = tgt_s; - - // Skip over the index of POINTER: - index_cblc += 1; - - // Pick up the pointer, if any - size_t pointer = 0; - int overflow = 0; - if( ref[INDEX_OF_POINTER].field ) - { - __int128 p = __gg__int128_from_qualified_field( - ref[INDEX_OF_POINTER].field, - ref[INDEX_OF_POINTER].offset, - ref[INDEX_OF_POINTER].size); - if( p < 1 || static_cast<unsigned __int128>(p) > dest_length ) - { - overflow = 1; - } - else - { - pointer = static_cast<size_t>(p - 1); - } - } - - // Make sure that the destination pointer is within the destination - if( !overflow && pointer < dest_length ) - { - // We are go for looping through identifier-2 values: - - size_t index_int = 0; - - // Pick up the number of identifier-2 values - size_t N = integers[index_int++]; - - std::string delimiter; - for( size_t i=0; i<N; i++ ) - { - // Pick up the number of M identifier-1 values for this list of - // identifier-2 values: - size_t M = integers[index_int++]; - - // Pick up the identifier_2 DELIMITED BY value - char fig_id2[4]; - size_t str_id2_size; - const char *str_id2 = string_operand(ref[index_cblc], - charmap, - 1, - fig_id2, - &str_id2_size); - index_cblc += 1; - - // Preserve the delimiter in case it overlaps the target. - delimiter.assign(str_id2, str_id2_size); - - for(size_t j=0; j<M; j++) - { - // Pick up the next id-1 source string for the current id-2 delimiter - char fig_id1[4]; - size_t str_id1_size; - const char *str_id1 = string_operand(ref[index_cblc], - charmap, - 1, - fig_id1, - &str_id1_size); - index_cblc += 1; - - size_t nfound; - if( delimiter.empty() ) - { - // No given delimiter means DELIMITED BY SIZE - nfound = str_id1_size; - } - else if( delimiter.size() == 1 ) - { - // A one-character delimiter is overwhelmingly the common case. - const void *found = memchr(str_id1, - static_cast<unsigned char>(delimiter[0]), - str_id1_size); - nfound = found - ? static_cast<size_t>( - static_cast<const char *>(found) - str_id1) - : str_id1_size; - } - else - { - // We have an id2, so we look for it inside id1 - const char *found = std::search(str_id1, - str_id1 + str_id1_size, - delimiter.begin(), - delimiter.end()); - nfound = static_cast<size_t>(found - str_id1); - } - - // We have found id2 inside id1 at location nfound. - size_t available = dest_length - pointer; - size_t count = std::min(nfound, available); - memmove(dest + pointer, str_id1, count); - pointer += count; - if( count != nfound ) - { - overflow = 1; - } - if( overflow ) - { - break; - } - } - if( overflow ) - { - break; - } - } - - // Update the pointer, if there is one - if( ref[INDEX_OF_POINTER].field ) - { - __gg__int128_to_qualified_field(ref[INDEX_OF_POINTER].field, - ref[INDEX_OF_POINTER].offset, - ref[INDEX_OF_POINTER].size, - (__int128)(pointer+1), - 0, - truncation_e); - } - } - else - { - // The initial pointer is not inside the destination - overflow = 1; - } - - return overflow; - } - -static size_t -find_string_24(const char *str_id1, - size_t str_id1_size, - const char *str_id2, - size_t str_id2_size, - size_t stride) - { - size_t id1_characters = str_id1_size / stride; - size_t id2_characters = str_id2_size / stride; - - if( id2_characters == 0 || id2_characters > id1_characters ) - { - return id1_characters; - } - - size_t last = id1_characters - id2_characters; - if( stride == 2 ) - { - uint16_t first = load_unaligned<uint16_t>(str_id2); - for(size_t i=0; i<=last; i++) - { - const char *candidate = str_id1 + i * stride; - if( load_unaligned<uint16_t>(candidate) == first - && ( id2_characters == 1 - || memcmp(candidate, str_id2, str_id2_size) == 0) ) - { - return i; - } - } - } - else - { - uint32_t first = load_unaligned<uint32_t>(str_id2); - for(size_t i=0; i<=last; i++) - { - const char *candidate = str_id1 + i * stride; - if( load_unaligned<uint32_t>(candidate) == first - && ( id2_characters == 1 - || memcmp(candidate, str_id2, str_id2_size) == 0) ) - { - return i; - } - } - } - return id1_characters; - } - -extern "C" -int -__gg__string_24(const size_t integers[], const cblc_referlet_t *ref) - { - // The first integer is the count of identifier-2 values. Call it N - // The following N integers are the counts of each of the identifier-1 - // values, - // one for each identifier-1. Call them M. - - // The first refer is the target - // The second refer is the pointer - // The third refer is identifier-2 for N1 - // That's followed by M1 identifier-1 values - // That's followed by identifier2 for N2 - // And so on - - static const int INDEX_OF_POINTER = 1; - - size_t index_cblc = 0 ; - - // Pick up the target - const cblc_field_t *tgt = ref[index_cblc].field; - - // Pick up the target encoding, which according to the ISO specification - // controls all the parameters. - cbl_encoding_t tgt_encoding = tgt->encoding; - charmap_t *charmap = __gg__get_charmap(tgt_encoding); - size_t stride = static_cast<size_t>(charmap->stride()); - - // Pick up the rest of the parameters - size_t tgt_o = ref[index_cblc].offset; - size_t tgt_s = ref[index_cblc].size; - index_cblc += 1; - - char *dest = as_chars(tgt->data + tgt_o); - size_t dest_length = tgt_s / stride; - - // Skip over the index of POINTER: - index_cblc += 1; - - // Pick up the pointer, if any - size_t pointer = 0; - int overflow = 0; - if( ref[INDEX_OF_POINTER].field ) - { - __int128 p = __gg__int128_from_qualified_field( - ref[INDEX_OF_POINTER].field, - ref[INDEX_OF_POINTER].offset, - ref[INDEX_OF_POINTER].size); - if( p < 1 || static_cast<unsigned __int128>(p) > dest_length ) - { - overflow = 1; - } - else - { - pointer = static_cast<size_t>(p - 1); - } - } - - // Make sure that the destination pointer is within the destination - if( !overflow && pointer < dest_length ) - { - // We are go for looping through identifier-2 values: - - size_t index_int = 0; - - // Pick up the number of identifier-2 values - size_t N = integers[index_int++]; - - std::string delimiter; - for(size_t i=0; i<N; i++) - { - // Pick up the number of M identifier-1 values for this list of - // identifier-2 values: - size_t M = integers[index_int++]; - - // Pick up the identifier_2 DELIMITED BY value - char fig_id2[4]; - size_t str_id2_size; - const char *str_id2 = string_operand(ref[index_cblc], - charmap, - stride, - fig_id2, - &str_id2_size); - index_cblc += 1; - - // Preserve the delimiter in case it overlaps the target. - delimiter.assign(str_id2, str_id2_size); - - for(size_t j=0; j<M; j++) - { - // Pick up the next id-1 source string for the current id-2 delimiter - char fig_id1[4]; - size_t str_id1_size; - const char *str_id1 = string_operand(ref[index_cblc], - charmap, - stride, - fig_id1, - &str_id1_size); - index_cblc += 1; - - size_t nfound; - if( delimiter.empty() ) - { - // No given delimiter means DELIMITED BY SIZE - nfound = str_id1_size / stride; - } - else - { - // We have an id2, so we look for it inside id1 - nfound = find_string_24(str_id1, - str_id1_size, - delimiter.data(), - delimiter.size(), - stride); - } - - // We have found id2 inside id1 at location nfound. - size_t available = dest_length - pointer; - size_t count = std::min(nfound, available); - memmove(dest + pointer * stride, - str_id1, - count * stride); - pointer += count; - if( count != nfound ) - { - overflow = 1; - } - if( overflow ) - { - break; - } - } - if( overflow ) - { - break; - } - } - - // Update the pointer, if there is one - if( ref[INDEX_OF_POINTER].field ) - { - __gg__int128_to_qualified_field(ref[INDEX_OF_POINTER].field, - ref[INDEX_OF_POINTER].offset, - ref[INDEX_OF_POINTER].size, - (__int128)(pointer+1), - 0, - truncation_e); - } - } - else - { - // The initial pointer is not inside the destination - overflow = 1; - } - - return overflow; - } - -extern "C" -int -__gg__string(const size_t integers[], const cblc_referlet_t *ref) - { - const charmap_t *charmap = __gg__get_charmap(ref[0].field->encoding); - return charmap->stride() == 1 - ? __gg__string_1(integers, ref) - : __gg__string_24(integers, ref); - } - static void display_both(cblc_field_t *field, @@ -10121,489 +9677,6 @@ __gg__assign_value_from_stack(cblc_field_t *dest, __int128 parameter) } } -struct unstring_finder_1 - { - static size_t - find(const char *str_id1, - size_t str_id1_size, - const char *str_id2, - size_t str_id2_size, - size_t) - { - if( str_id2_size == 1 ) - { - const void *found = memchr(str_id1, - static_cast<unsigned char>(str_id2[0]), - str_id1_size); - return found - ? static_cast<size_t>(static_cast<const char *>(found) - str_id1) - : str_id1_size; - } - - const char *found = std::search(str_id1, - str_id1 + str_id1_size, - str_id2, - str_id2 + str_id2_size); - return static_cast<size_t>(found - str_id1); - } - }; - -struct unstring_finder_24 - { - static size_t - find(const char *str_id1, - size_t str_id1_size, - const char *str_id2, - size_t str_id2_size, - size_t stride) - { - return find_string_24(str_id1, - str_id1_size, - str_id2, - str_id2_size, - stride); - } - }; - -template <typename finder_t> -static int -unstring_fixed( const cblc_referlet_t *id2, - const cblc_referlet_t *id4, - const cblc_referlet_t *id5, - const cblc_referlet_t *id6, - const cblc_field_t *id1, // The string being unstring - size_t id1_o, - size_t id1_s, - size_t ndelimiteds, // The number of DELIMITED entries - const char *all_flags, // ALL flags, one per ndelimiteds - size_t nreceivers, // The number of DELIMITER receivers - cblc_field_t *id7, // Character index, start and end - size_t id7_o, - size_t id7_s, - cblc_field_t *id8, // Count of identifier-4 updates - size_t id8_o, - size_t id8_s, - size_t stride_id1) - { - // The names of the parameters are based on the ISO 1989:2014 specification. - - // There are complexities because of figurative constants, including the - // LOW-VALUE figurative constant, which precludes the use of string - // operations that would be confused by embedded NUL characters. Dammit. - - // For each delimiter, there is an identifier-4 receiver that must be - // resolved. Each might have an identifier-5 delimiter, and each might have - // an identifier-6 count. - - // Initialize the state variables - int overflow = 0; - int tally = 0; - size_t pointer = 1; - size_t nreceiver; - size_t left=0; - size_t right=0; - - std::string str_id1; - std::vector<std::string> delimiters; - - charmap_t *charmap_id1 = __gg__get_charmap(id1->encoding); - - if( id8 ) - { - tally = (int)__gg__int128_from_qualified_field(id8, - id8_o, - id8_s); - } - - if( id7 ) - { - __int128 p = __gg__int128_from_qualified_field(id7, - id7_o, - id7_s); - if( p < 1 - || static_cast<unsigned __int128>(p) - > std::numeric_limits<size_t>::max() ) - { - overflow = 1; - goto done; - } - pointer = static_cast<size_t>(p); - } - - // As per the spec, if the string is zero-length; we are done. - if( id1_s == 0 ) - { - goto done; - } - - // As per the spec, we have an overflow condition if pointer is out of - // range: - if( pointer > id1_s/stride_id1 ) - { - overflow = 1; - goto done; - } - // pointer is one-based throughout; don't forget that - - /* The earlier implementation converted everything to UTF32. The - fixed-width entry points handle their encodings directly. */ - - // Preserve identifier-1 because a receiving item can overlap it. - str_id1.assign(as_chars(id1->data + id1_o), id1_s); - left = pointer-1; - right = str_id1.size() / stride_id1; - if( ndelimiteds == 0 ) - { - // There are no DELIMITED BY identifier-2 values, so we just peel off - // characters from identifier-1 and put them into each identifier-4: - for( size_t receiver=0; receiver<nreceivers; receiver++ ) - { - if( left >= right ) - { - // We have run out of input characters. - break; - } - // We will peel off enough characters to fit the receiving id4: - size_t id_4_size = id4[receiver].size/stride_id1; - if( id4[receiver].field->attr & separate_e ) - { - // The receiver is NumericDisplay with a separate sign, so, as per - // the spec, we reduce the size by one character. - if( id_4_size ) - { - id_4_size -= 1; - } - } - - // Make sure id_4_size doesn't take us past the end of the universe - if( left + id_4_size > right ) - { - id_4_size = right - left; - } - - // Put the specified string into place: - __gg__move_literala(id4[receiver].field, - id4[receiver].offset, - id4[receiver].size, - truncation_e, - str_id1.data() + left * stride_id1, - id_4_size * stride_id1); - // Update the state variables: - left += id_4_size; - pointer += id_4_size; - tally += 1; - } - goto done; - } - - // Arriving here means there is some number of ndelimiteds - - // Preserve the delimiters because receiving items can overlap them. - delimiters.reserve(ndelimiteds); - for( size_t i=0; i<ndelimiteds; i++ ) - { - char figurative[4]; - size_t delimiter_size; - const char *delimiter = string_operand(id2[i], - charmap_id1, - stride_id1, - figurative, - &delimiter_size); - delimiters.emplace_back(delimiter, delimiter_size); - } - - nreceiver = 0; - while( left < right ) - { - // If we've used up all receivers, we bail at this point - if( nreceiver >= nreceivers ) - { - break; - } - - // Starting at 'left', see if we can find any of the delimiters. For each - // 'left' position, we look through all of the delimiters, - - size_t best_delimiter = ndelimiteds; - size_t best_leftmost = right; // This is the location of the start of ALL - size_t best_location = right; // This is the location of the last of ALL - for( size_t i=0; i<ndelimiteds; i++ ) - { - const std::string &str_id2 = delimiters[i]; - if( str_id2.empty() ) - { - continue; - } - size_t nfound = left + finder_t::find( - str_id1.data() + left * stride_id1, - (right - left) * stride_id1, - str_id2.data(), - str_id2.size(), - stride_id1); - if( nfound != right ) - { - // We found a delimiter - if( nfound > best_leftmost ) - { - // This delimiter lives to the right of the best one we found so far. - // Ignore it, and proceed to the next delimiter. - continue; - } - // This delimiter is the leftmost we've seen so far: - best_delimiter = i; - best_leftmost = nfound; - best_location = nfound; - - if( all_flags[i] == ascii_1 ) - { - // This delimiter is flagged as ALL, so we need to see if we have - // a flock of them: - size_t delimiter_characters = str_id2.size() / stride_id1; - size_t next = nfound + delimiter_characters; - while( delimiter_characters <= right - next - && memcmp(str_id1.data() + next * stride_id1, - str_id2.data(), - str_id2.size()) == 0 ) - { - // We found another consecutive one at next: - best_location = next; - next += delimiter_characters; - } - } - } - } - - if( best_delimiter == ndelimiteds ) - { - // We were unable to find a delimiter, so we eat up the remainder - // of the sender: - best_leftmost = right; - best_location = right; - } - - // Apply what we have learned to the next receiver: - - size_t examined = best_leftmost - left; - - // Put the specified string into place: - __gg__move_literala(id4[nreceiver].field, - id4[nreceiver].offset, - id4[nreceiver].size, - truncation_e, - str_id1.data() + left * stride_id1, - examined * stride_id1); - // Update the left edge - left = best_location + (best_delimiter != ndelimiteds - ? delimiters[best_delimiter].size() / stride_id1 - : 0) ; - if( id5[nreceiver].field ) - { - // The caller wants to know what the delimiter was: - if( best_delimiter != ndelimiteds ) - { - __gg__move_literala(id5[nreceiver].field, - id5[nreceiver].offset, - id5[nreceiver].size, - truncation_e, - delimiters[best_delimiter].data(), - delimiters[best_delimiter].size()); - } - else - { - // We didn't find a delimiter - __gg__move_literala(id5[nreceiver].field, - id5[nreceiver].offset, - id5[nreceiver].size, - truncation_e, - "", - 0); - } - } - - if( id6[nreceiver].field ) - { - __gg__int128_to_qualified_field(id6[nreceiver].field, - id6[nreceiver].offset, - id6[nreceiver].size, - (__int128)examined, - 0, - truncation_e); - } - - // Update the state variables: - tally += 1; - nreceiver += 1; - if( best_delimiter != ndelimiteds ) - { - pointer = left+1 ; - } - } - -done: - - if( id8 ) - { - __gg__int128_to_qualified_field(id8, - id8_o, - id8_s, - (__int128)tally, - 0, - truncation_e); - } - - if( id7 ) - { - __gg__int128_to_qualified_field(id7, - id7_o, - id7_s, - (__int128)pointer, - 0, - truncation_e); - } - - if( left < right ) - { - overflow = 1; - } - - return overflow; - } - -extern "C" -int -__gg__unstring_1( const cblc_referlet_t *id2, - const cblc_referlet_t *id4, - const cblc_referlet_t *id5, - const cblc_referlet_t *id6, - const cblc_field_t *id1, - size_t id1_o, - size_t id1_s, - size_t ndelimiteds, - const char *all_flags, - size_t nreceivers, - cblc_field_t *id7, - size_t id7_o, - size_t id7_s, - cblc_field_t *id8, - size_t id8_o, - size_t id8_s) - { - return unstring_fixed<unstring_finder_1>(id2, - id4, - id5, - id6, - id1, - id1_o, - id1_s, - ndelimiteds, - all_flags, - nreceivers, - id7, - id7_o, - id7_s, - id8, - id8_o, - id8_s, - 1); - } - -extern "C" -int -__gg__unstring_24( const cblc_referlet_t *id2, - const cblc_referlet_t *id4, - const cblc_referlet_t *id5, - const cblc_referlet_t *id6, - const cblc_field_t *id1, - size_t id1_o, - size_t id1_s, - size_t ndelimiteds, - const char *all_flags, - size_t nreceivers, - cblc_field_t *id7, - size_t id7_o, - size_t id7_s, - cblc_field_t *id8, - size_t id8_o, - size_t id8_s) - { - size_t stride = static_cast<size_t>( - __gg__get_charmap(id1->encoding)->stride()); - return unstring_fixed<unstring_finder_24>(id2, - id4, - id5, - id6, - id1, - id1_o, - id1_s, - ndelimiteds, - all_flags, - nreceivers, - id7, - id7_o, - id7_s, - id8, - id8_o, - id8_s, - stride); - } - -extern "C" -int -__gg__unstring( const cblc_referlet_t *id2, - const cblc_referlet_t *id4, - const cblc_referlet_t *id5, - const cblc_referlet_t *id6, - const cblc_field_t *id1, - size_t id1_o, - size_t id1_s, - size_t ndelimiteds, - const char *all_flags, - size_t nreceivers, - cblc_field_t *id7, - size_t id7_o, - size_t id7_s, - cblc_field_t *id8, - size_t id8_o, - size_t id8_s) - { - const charmap_t *charmap = __gg__get_charmap(id1->encoding); - if( charmap->stride() == 1 ) - { - return __gg__unstring_1(id2, - id4, - id5, - id6, - id1, - id1_o, - id1_s, - ndelimiteds, - all_flags, - nreceivers, - id7, - id7_o, - id7_s, - id8, - id8_o, - id8_s); - } - return __gg__unstring_24(id2, - id4, - id5, - id6, - id1, - id1_o, - id1_s, - ndelimiteds, - all_flags, - nreceivers, - id7, - id7_o, - id7_s, - id8, - id8_o, - id8_s); - } - static std::set<void *> to_be_canceled; extern "C" @@ -14530,3 +13603,4 @@ __gg__move_stash_release(const unsigned char *stash) move_stashes.depth -= 1; } + diff --git a/libgcobol/libgcobol.h b/libgcobol/libgcobol.h index 49f1b1f47682..35919e4ab818 100644 --- a/libgcobol/libgcobol.h +++ b/libgcobol/libgcobol.h @@ -158,4 +158,11 @@ extern "C" __int128 __gg__int128_to_int128_rounded( cbl_round_t rounded, __int128 factor, int *compute_error); +extern "C" int __gg__move_literala( cblc_field_t *field, + size_t field_offset, + size_t field_size, + cbl_round_t rounded_, + const char *str, + size_t strlen ); + #endif diff --git a/libgcobol/unstring.cc b/libgcobol/unstring.cc new file mode 100644 index 000000000000..10701e225de0 --- /dev/null +++ b/libgcobol/unstring.cc @@ -0,0 +1,1022 @@ +/* + * 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 + * * 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 <cwctype> +#include <limits> + +#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 <langinfo.h> + +#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" + +static inline char * +as_chars(unsigned char *p) + { + return reinterpret_cast<char *>(p); + } + +template <typename T> +static T +load_unaligned(const void *p) + { + T value; + memcpy(&value, p, sizeof(value)); + return value; + } + +// The compiler ensures that every operand uses the target encoding, so an +// ordinary operand can be used directly. +static inline const char * +string_operand(const cblc_referlet_t &refer, + charmap_t *charmap, + size_t stride, + char figurative[4], + size_t *size) + { + const cblc_field_t *field = refer.field; + if( !field ) + { + *size = 0; + return figurative; + } + + cbl_figconst_t figconst = + static_cast<cbl_figconst_t>(field->attr & FIGCONST_MASK); + // We have a corner case to deal with: + if( !figconst + && field->name[0] == 'N' + && strcmp(field->name, "NULLS") == 0 ) + { + figconst = null_value_e; + } + + if( figconst ) + { + cbl_char_t figchar = '\0'; + switch( figconst ) + { + case low_value_e : + figchar = charmap->low_value_character(); + break; + case zero_value_e : + figchar = charmap->mapped_character(ascii_0); + break; + case space_value_e : + figchar = charmap->mapped_character(ascii_space); + break; + case quote_value_e : + figchar = charmap->quote_character(); + break; + case high_value_e : + figchar = charmap->high_value_character(); + break; + case null_value_e: + break; + default: + abort(); + break; + } + charmap->putch(figchar, figurative, size_t(0)); + *size = stride; + return figurative; + } + + *size = refer.size; + return as_chars(field->data + refer.offset); + } + +extern "C" +int +__gg__string_1(const size_t integers[], const cblc_referlet_t *ref) + { + // The first integer is the count of identifier-2 values. Call it N + // The following N integers are the counts of each of the identifier-1 + // values, + // one for each identifier-1. Call them M. + + // The first refer is the target + // The second refer is the pointer + // The third refer is identifier-2 for N1 + // That's followed by M1 identifier-1 values + // That's followed by identifier2 for N2 + // And so on + + static const int INDEX_OF_POINTER = 1; + + size_t index_cblc = 0 ; + + // Pick up the target + const cblc_field_t *tgt = ref[index_cblc].field; + + // Pick up the target encoding, which according to the ISO specification + // controls all the parameters. + cbl_encoding_t tgt_encoding = tgt->encoding; + charmap_t *charmap = __gg__get_charmap(tgt_encoding); + + // Pick up the rest of the parameters + size_t tgt_o = ref[index_cblc].offset; + size_t tgt_s = ref[index_cblc].size; + index_cblc += 1; + + char *dest = as_chars(tgt->data + tgt_o); + size_t dest_length = tgt_s; + + // Skip over the index of POINTER: + index_cblc += 1; + + // Pick up the pointer, if any + size_t pointer = 0; + int overflow = 0; + if( ref[INDEX_OF_POINTER].field ) + { + __int128 p = __gg__int128_from_qualified_field( + ref[INDEX_OF_POINTER].field, + ref[INDEX_OF_POINTER].offset, + ref[INDEX_OF_POINTER].size); + if( p < 1 || static_cast<unsigned __int128>(p) > dest_length ) + { + overflow = 1; + } + else + { + pointer = static_cast<size_t>(p - 1); + } + } + + // Make sure that the destination pointer is within the destination + if( !overflow && pointer < dest_length ) + { + // We are go for looping through identifier-2 values: + + size_t index_int = 0; + + // Pick up the number of identifier-2 values + size_t N = integers[index_int++]; + + std::string delimiter; + for( size_t i=0; i<N; i++ ) + { + // Pick up the number of M identifier-1 values for this list of + // identifier-2 values: + size_t M = integers[index_int++]; + + // Pick up the identifier_2 DELIMITED BY value + char fig_id2[4]; + size_t str_id2_size; + const char *str_id2 = string_operand(ref[index_cblc], + charmap, + 1, + fig_id2, + &str_id2_size); + index_cblc += 1; + + // Preserve the delimiter in case it overlaps the target. + delimiter.assign(str_id2, str_id2_size); + + for(size_t j=0; j<M; j++) + { + // Pick up the next id-1 source string for the current id-2 delimiter + char fig_id1[4]; + size_t str_id1_size; + const char *str_id1 = string_operand(ref[index_cblc], + charmap, + 1, + fig_id1, + &str_id1_size); + index_cblc += 1; + + size_t nfound; + if( delimiter.empty() ) + { + // No given delimiter means DELIMITED BY SIZE + nfound = str_id1_size; + } + else if( delimiter.size() == 1 ) + { + // A one-character delimiter is overwhelmingly the common case. + const void *found = memchr(str_id1, + static_cast<unsigned char>(delimiter[0]), + str_id1_size); + nfound = found + ? static_cast<size_t>( + static_cast<const char *>(found) - str_id1) + : str_id1_size; + } + else + { + // We have an id2, so we look for it inside id1 + const char *found = std::search(str_id1, + str_id1 + str_id1_size, + delimiter.begin(), + delimiter.end()); + nfound = static_cast<size_t>(found - str_id1); + } + + // We have found id2 inside id1 at location nfound. + size_t available = dest_length - pointer; + size_t count = std::min(nfound, available); + memmove(dest + pointer, str_id1, count); + pointer += count; + if( count != nfound ) + { + overflow = 1; + } + if( overflow ) + { + break; + } + } + if( overflow ) + { + break; + } + } + + // Update the pointer, if there is one + if( ref[INDEX_OF_POINTER].field ) + { + __gg__int128_to_qualified_field(ref[INDEX_OF_POINTER].field, + ref[INDEX_OF_POINTER].offset, + ref[INDEX_OF_POINTER].size, + (__int128)(pointer+1), + 0, + truncation_e); + } + } + else + { + // The initial pointer is not inside the destination + overflow = 1; + } + + return overflow; + } + +static size_t +find_string_24(const char *str_id1, + size_t str_id1_size, + const char *str_id2, + size_t str_id2_size, + size_t stride) + { + size_t id1_characters = str_id1_size / stride; + size_t id2_characters = str_id2_size / stride; + + if( id2_characters == 0 || id2_characters > id1_characters ) + { + return id1_characters; + } + + size_t last = id1_characters - id2_characters; + if( stride == 2 ) + { + uint16_t first = load_unaligned<uint16_t>(str_id2); + for(size_t i=0; i<=last; i++) + { + const char *candidate = str_id1 + i * stride; + if( load_unaligned<uint16_t>(candidate) == first + && ( id2_characters == 1 + || memcmp(candidate, str_id2, str_id2_size) == 0) ) + { + return i; + } + } + } + else + { + uint32_t first = load_unaligned<uint32_t>(str_id2); + for(size_t i=0; i<=last; i++) + { + const char *candidate = str_id1 + i * stride; + if( load_unaligned<uint32_t>(candidate) == first + && ( id2_characters == 1 + || memcmp(candidate, str_id2, str_id2_size) == 0) ) + { + return i; + } + } + } + return id1_characters; + } + +extern "C" +int +__gg__string_24(const size_t integers[], const cblc_referlet_t *ref) + { + // The first integer is the count of identifier-2 values. Call it N + // The following N integers are the counts of each of the identifier-1 + // values, + // one for each identifier-1. Call them M. + + // The first refer is the target + // The second refer is the pointer + // The third refer is identifier-2 for N1 + // That's followed by M1 identifier-1 values + // That's followed by identifier2 for N2 + // And so on + + static const int INDEX_OF_POINTER = 1; + + size_t index_cblc = 0 ; + + // Pick up the target + const cblc_field_t *tgt = ref[index_cblc].field; + + // Pick up the target encoding, which according to the ISO specification + // controls all the parameters. + cbl_encoding_t tgt_encoding = tgt->encoding; + charmap_t *charmap = __gg__get_charmap(tgt_encoding); + size_t stride = static_cast<size_t>(charmap->stride()); + + // Pick up the rest of the parameters + size_t tgt_o = ref[index_cblc].offset; + size_t tgt_s = ref[index_cblc].size; + index_cblc += 1; + + char *dest = as_chars(tgt->data + tgt_o); + size_t dest_length = tgt_s / stride; + + // Skip over the index of POINTER: + index_cblc += 1; + + // Pick up the pointer, if any + size_t pointer = 0; + int overflow = 0; + if( ref[INDEX_OF_POINTER].field ) + { + __int128 p = __gg__int128_from_qualified_field( + ref[INDEX_OF_POINTER].field, + ref[INDEX_OF_POINTER].offset, + ref[INDEX_OF_POINTER].size); + if( p < 1 || static_cast<unsigned __int128>(p) > dest_length ) + { + overflow = 1; + } + else + { + pointer = static_cast<size_t>(p - 1); + } + } + + // Make sure that the destination pointer is within the destination + if( !overflow && pointer < dest_length ) + { + // We are go for looping through identifier-2 values: + + size_t index_int = 0; + + // Pick up the number of identifier-2 values + size_t N = integers[index_int++]; + + std::string delimiter; + for(size_t i=0; i<N; i++) + { + // Pick up the number of M identifier-1 values for this list of + // identifier-2 values: + size_t M = integers[index_int++]; + + // Pick up the identifier_2 DELIMITED BY value + char fig_id2[4]; + size_t str_id2_size; + const char *str_id2 = string_operand(ref[index_cblc], + charmap, + stride, + fig_id2, + &str_id2_size); + index_cblc += 1; + + // Preserve the delimiter in case it overlaps the target. + delimiter.assign(str_id2, str_id2_size); + + for(size_t j=0; j<M; j++) + { + // Pick up the next id-1 source string for the current id-2 delimiter + char fig_id1[4]; + size_t str_id1_size; + const char *str_id1 = string_operand(ref[index_cblc], + charmap, + stride, + fig_id1, + &str_id1_size); + index_cblc += 1; + + size_t nfound; + if( delimiter.empty() ) + { + // No given delimiter means DELIMITED BY SIZE + nfound = str_id1_size / stride; + } + else + { + // We have an id2, so we look for it inside id1 + nfound = find_string_24(str_id1, + str_id1_size, + delimiter.data(), + delimiter.size(), + stride); + } + + // We have found id2 inside id1 at location nfound. + size_t available = dest_length - pointer; + size_t count = std::min(nfound, available); + memmove(dest + pointer * stride, + str_id1, + count * stride); + pointer += count; + if( count != nfound ) + { + overflow = 1; + } + if( overflow ) + { + break; + } + } + if( overflow ) + { + break; + } + } + + // Update the pointer, if there is one + if( ref[INDEX_OF_POINTER].field ) + { + __gg__int128_to_qualified_field(ref[INDEX_OF_POINTER].field, + ref[INDEX_OF_POINTER].offset, + ref[INDEX_OF_POINTER].size, + (__int128)(pointer+1), + 0, + truncation_e); + } + } + else + { + // The initial pointer is not inside the destination + overflow = 1; + } + + return overflow; + } + +extern "C" +int +__gg__string(const size_t integers[], const cblc_referlet_t *ref) + { + const charmap_t *charmap = __gg__get_charmap(ref[0].field->encoding); + return charmap->stride() == 1 + ? __gg__string_1(integers, ref) + : __gg__string_24(integers, ref); + } + +struct unstring_finder_1 + { + static size_t + find(const char *str_id1, + size_t str_id1_size, + const char *str_id2, + size_t str_id2_size, + size_t) + { + if( str_id2_size == 1 ) + { + const void *found = memchr(str_id1, + static_cast<unsigned char>(str_id2[0]), + str_id1_size); + return found + ? static_cast<size_t>(static_cast<const char *>(found) - str_id1) + : str_id1_size; + } + + const char *found = std::search(str_id1, + str_id1 + str_id1_size, + str_id2, + str_id2 + str_id2_size); + return static_cast<size_t>(found - str_id1); + } + }; + +struct unstring_finder_24 + { + static size_t + find(const char *str_id1, + size_t str_id1_size, + const char *str_id2, + size_t str_id2_size, + size_t stride) + { + return find_string_24(str_id1, + str_id1_size, + str_id2, + str_id2_size, + stride); + } + }; + +template <typename finder_t> +static int +unstring_fixed( const cblc_referlet_t *id2, + const cblc_referlet_t *id4, + const cblc_referlet_t *id5, + const cblc_referlet_t *id6, + const cblc_field_t *id1, // The string being unstring + size_t id1_o, + size_t id1_s, + size_t ndelimiteds, // The number of DELIMITED entries + const char *all_flags, // ALL flags, one per ndelimiteds + size_t nreceivers, // The number of DELIMITER receivers + cblc_field_t *id7, // Character index, start and end + size_t id7_o, + size_t id7_s, + cblc_field_t *id8, // Count of identifier-4 updates + size_t id8_o, + size_t id8_s, + size_t stride_id1) + { + // The names of the parameters are based on the ISO 1989:2014 specification. + + // There are complexities because of figurative constants, including the + // LOW-VALUE figurative constant, which precludes the use of string + // operations that would be confused by embedded NUL characters. Dammit. + + // For each delimiter, there is an identifier-4 receiver that must be + // resolved. Each might have an identifier-5 delimiter, and each might have + // an identifier-6 count. + + // Initialize the state variables + int overflow = 0; + int tally = 0; + size_t pointer = 1; + size_t nreceiver; + size_t left=0; + size_t right=0; + + std::string str_id1; + std::vector<std::string> delimiters; + + charmap_t *charmap_id1 = __gg__get_charmap(id1->encoding); + + if( id8 ) + { + tally = (int)__gg__int128_from_qualified_field(id8, + id8_o, + id8_s); + } + + if( id7 ) + { + __int128 p = __gg__int128_from_qualified_field(id7, + id7_o, + id7_s); + if( p < 1 + || static_cast<unsigned __int128>(p) + > std::numeric_limits<size_t>::max() ) + { + overflow = 1; + goto done; + } + pointer = static_cast<size_t>(p); + } + + // As per the spec, if the string is zero-length; we are done. + if( id1_s == 0 ) + { + goto done; + } + + // As per the spec, we have an overflow condition if pointer is out of + // range: + if( pointer > id1_s/stride_id1 ) + { + overflow = 1; + goto done; + } + // pointer is one-based throughout; don't forget that + + /* The earlier implementation converted everything to UTF32. The + fixed-width entry points handle their encodings directly. */ + + // Preserve identifier-1 because a receiving item can overlap it. + str_id1.assign(as_chars(id1->data + id1_o), id1_s); + left = pointer-1; + right = str_id1.size() / stride_id1; + if( ndelimiteds == 0 ) + { + // There are no DELIMITED BY identifier-2 values, so we just peel off + // characters from identifier-1 and put them into each identifier-4: + for( size_t receiver=0; receiver<nreceivers; receiver++ ) + { + if( left >= right ) + { + // We have run out of input characters. + break; + } + // We will peel off enough characters to fit the receiving id4: + size_t id_4_size = id4[receiver].size/stride_id1; + if( id4[receiver].field->attr & separate_e ) + { + // The receiver is NumericDisplay with a separate sign, so, as per + // the spec, we reduce the size by one character. + if( id_4_size ) + { + id_4_size -= 1; + } + } + + // Make sure id_4_size doesn't take us past the end of the universe + if( left + id_4_size > right ) + { + id_4_size = right - left; + } + + // Put the specified string into place: + __gg__move_literala(id4[receiver].field, + id4[receiver].offset, + id4[receiver].size, + truncation_e, + str_id1.data() + left * stride_id1, + id_4_size * stride_id1); + // Update the state variables: + left += id_4_size; + pointer += id_4_size; + tally += 1; + } + goto done; + } + + // Arriving here means there is some number of ndelimiteds + + // Preserve the delimiters because receiving items can overlap them. + delimiters.reserve(ndelimiteds); + for( size_t i=0; i<ndelimiteds; i++ ) + { + char figurative[4]; + size_t delimiter_size; + const char *delimiter = string_operand(id2[i], + charmap_id1, + stride_id1, + figurative, + &delimiter_size); + delimiters.emplace_back(delimiter, delimiter_size); + } + + nreceiver = 0; + while( left < right ) + { + // If we've used up all receivers, we bail at this point + if( nreceiver >= nreceivers ) + { + break; + } + + // Starting at 'left', see if we can find any of the delimiters. For each + // 'left' position, we look through all of the delimiters, + + size_t best_delimiter = ndelimiteds; + size_t best_leftmost = right; // This is the location of the start of ALL + size_t best_location = right; // This is the location of the last of ALL + for( size_t i=0; i<ndelimiteds; i++ ) + { + const std::string &str_id2 = delimiters[i]; + if( str_id2.empty() ) + { + continue; + } + size_t nfound = left + finder_t::find( + str_id1.data() + left * stride_id1, + (right - left) * stride_id1, + str_id2.data(), + str_id2.size(), + stride_id1); + if( nfound != right ) + { + // We found a delimiter + if( nfound > best_leftmost ) + { + // This delimiter lives to the right of the best one we found so far. + // Ignore it, and proceed to the next delimiter. + continue; + } + // This delimiter is the leftmost we've seen so far: + best_delimiter = i; + best_leftmost = nfound; + best_location = nfound; + + if( all_flags[i] == ascii_1 ) + { + // This delimiter is flagged as ALL, so we need to see if we have + // a flock of them: + size_t delimiter_characters = str_id2.size() / stride_id1; + size_t next = nfound + delimiter_characters; + while( delimiter_characters <= right - next + && memcmp(str_id1.data() + next * stride_id1, + str_id2.data(), + str_id2.size()) == 0 ) + { + // We found another consecutive one at next: + best_location = next; + next += delimiter_characters; + } + } + } + } + + if( best_delimiter == ndelimiteds ) + { + // We were unable to find a delimiter, so we eat up the remainder + // of the sender: + best_leftmost = right; + best_location = right; + } + + // Apply what we have learned to the next receiver: + + size_t examined = best_leftmost - left; + + // Put the specified string into place: + __gg__move_literala(id4[nreceiver].field, + id4[nreceiver].offset, + id4[nreceiver].size, + truncation_e, + str_id1.data() + left * stride_id1, + examined * stride_id1); + // Update the left edge + left = best_location + (best_delimiter != ndelimiteds + ? delimiters[best_delimiter].size() / stride_id1 + : 0) ; + if( id5[nreceiver].field ) + { + // The caller wants to know what the delimiter was: + if( best_delimiter != ndelimiteds ) + { + __gg__move_literala(id5[nreceiver].field, + id5[nreceiver].offset, + id5[nreceiver].size, + truncation_e, + delimiters[best_delimiter].data(), + delimiters[best_delimiter].size()); + } + else + { + // We didn't find a delimiter + __gg__move_literala(id5[nreceiver].field, + id5[nreceiver].offset, + id5[nreceiver].size, + truncation_e, + "", + 0); + } + } + + if( id6[nreceiver].field ) + { + __gg__int128_to_qualified_field(id6[nreceiver].field, + id6[nreceiver].offset, + id6[nreceiver].size, + (__int128)examined, + 0, + truncation_e); + } + + // Update the state variables: + tally += 1; + nreceiver += 1; + if( best_delimiter != ndelimiteds ) + { + pointer = left+1 ; + } + } + +done: + + if( id8 ) + { + __gg__int128_to_qualified_field(id8, + id8_o, + id8_s, + (__int128)tally, + 0, + truncation_e); + } + + if( id7 ) + { + __gg__int128_to_qualified_field(id7, + id7_o, + id7_s, + (__int128)pointer, + 0, + truncation_e); + } + + if( left < right ) + { + overflow = 1; + } + + return overflow; + } + +extern "C" +int +__gg__unstring_1( const cblc_referlet_t *id2, + const cblc_referlet_t *id4, + const cblc_referlet_t *id5, + const cblc_referlet_t *id6, + const cblc_field_t *id1, + size_t id1_o, + size_t id1_s, + size_t ndelimiteds, + const char *all_flags, + size_t nreceivers, + cblc_field_t *id7, + size_t id7_o, + size_t id7_s, + cblc_field_t *id8, + size_t id8_o, + size_t id8_s) + { + return unstring_fixed<unstring_finder_1>(id2, + id4, + id5, + id6, + id1, + id1_o, + id1_s, + ndelimiteds, + all_flags, + nreceivers, + id7, + id7_o, + id7_s, + id8, + id8_o, + id8_s, + 1); + } + +extern "C" +int +__gg__unstring_24( const cblc_referlet_t *id2, + const cblc_referlet_t *id4, + const cblc_referlet_t *id5, + const cblc_referlet_t *id6, + const cblc_field_t *id1, + size_t id1_o, + size_t id1_s, + size_t ndelimiteds, + const char *all_flags, + size_t nreceivers, + cblc_field_t *id7, + size_t id7_o, + size_t id7_s, + cblc_field_t *id8, + size_t id8_o, + size_t id8_s) + { + size_t stride = static_cast<size_t>( + __gg__get_charmap(id1->encoding)->stride()); + return unstring_fixed<unstring_finder_24>(id2, + id4, + id5, + id6, + id1, + id1_o, + id1_s, + ndelimiteds, + all_flags, + nreceivers, + id7, + id7_o, + id7_s, + id8, + id8_o, + id8_s, + stride); + } + +extern "C" +int +__gg__unstring( const cblc_referlet_t *id2, + const cblc_referlet_t *id4, + const cblc_referlet_t *id5, + const cblc_referlet_t *id6, + const cblc_field_t *id1, + size_t id1_o, + size_t id1_s, + size_t ndelimiteds, + const char *all_flags, + size_t nreceivers, + cblc_field_t *id7, + size_t id7_o, + size_t id7_s, + cblc_field_t *id8, + size_t id8_o, + size_t id8_s) + { + const charmap_t *charmap = __gg__get_charmap(id1->encoding); + if( charmap->stride() == 1 ) + { + return __gg__unstring_1(id2, + id4, + id5, + id6, + id1, + id1_o, + id1_s, + ndelimiteds, + all_flags, + nreceivers, + id7, + id7_o, + id7_s, + id8, + id8_o, + id8_s); + } + return __gg__unstring_24(id2, + id4, + id5, + id6, + id1, + id1_o, + id1_s, + ndelimiteds, + all_flags, + nreceivers, + id7, + id7_o, + id7_s, + id8, + id8_o, + id8_s); + }
