| /* | |
| * 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); | |
| } |