<commit_before>//
//  File: %t-map.c
//  Summary: "map datatype"
//  Section: datatypes
//  Project: "Rebol 3 Interpreter and Run-time (Ren-C branch)"
//  Homepage: https://github.com/metaeducation/ren-c/
//
//=////////////////////////////////////////////////////////////////////////=//
//
// Copyright 2012 REBOL Technologies
// Copyright 2012-2017 Rebol Open Source Contributors
// REBOL is a trademark of REBOL Technologies
//
// See README.md and CREDITS.md for more information.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//=////////////////////////////////////////////////////////////////////////=//
//
// See %sys-map.h for an explanation of the map structure.
//

#include "sys-core.h"

//
//  CT_Map: C
//
REBINT CT_Map(const RELVAL *a, const RELVAL *b, REBINT mode)
{
    if (mode < 0) return -1;
    return 0 == Cmp_Array(a, b, FALSE);
}


//
//  Make_Map: C
//
// Makes a MAP block (that holds both keys and values).
// Capacity is measured in key-value pairings.
// A hash series is also created.
//
static REBMAP *Make_Map(REBCNT capacity)
{
    REBARR *pairlist = Make_Array_Core(capacity * 2, ARRAY_FLAG_PAIRLIST);
    SER(pairlist)->link.hashlist = Make_Hash_Sequence(capacity);

    return MAP(pairlist);
}


//
//  Find_Key_Hashed: C
//
// Returns hash index (either the match or the new one).
// A return of zero is valid (as a hash index);
//
// Wide: width of record (normally 2, a key and a value).
//
// Modes:
//     0 - search, return hash if found or not
//     1 - search, return hash, else return -1 if not
//     2 - search, return hash, else append value and return -1
//
REBINT Find_Key_Hashed(
    REBARR *array,
    REBSER *hashlist,
    const RELVAL *key, // !!! assumes key is followed by value(s) via ++
    REBSPC *specifier,
    REBCNT wide,
    REBOOL cased,
    REBYTE mode
) {
    REBCNT len = SER_LEN(hashlist);
    assert(len > 0);

    REBCNT hash = Hash_Value(key);

    // The REBCNT[] hash array size is chosen to try and make a large enough
    // table relative to the data that collisions will be hopefully not
    // frequent.  But they may still collide.  The method R3-Alpha chose to
    // deal with collisions was to have a "skip" amount that will go try
    // another hash bucket until the searched for key is found or a 0
    // entry in the hashlist is found.
    //
    // Note: if len and skip are co-primes is guaranteed that repeatedly
    // adding skip (and subtracting len when needed) all positions are
    // visited.  1 <= skip < len, and len is prime, so this is guaranteed.

    REBCNT skip = hash % (len - 1) + 1;

    hash = hash % len;

    // a 'zombie' is a key with void value, that may be overwritten.  Set to
    // len to indicate zombie not yet encountered.
    //
    REBCNT zombie = len;

    REBCNT uncased = len; // uncased match not yet encountered

    // Scan hash table for match:

    REBCNT *hashes = SER_HEAD(REBCNT, hashlist);
    REBCNT n;
    RELVAL *val;

    if (ANY_WORD(key)) {
        while ((n = hashes[hash])) {
            val = ARR_AT(array, (n - 1) * wide);
            if (ANY_WORD(val) && VAL_WORD_SPELLING(key) == VAL_WORD_SPELLING(val))
                return hash;

            if (!cased && VAL_WORD_CANON(key) == VAL_WORD_CANON(val) && uncased == len) {
                uncased = hash;
            }
            else if (wide > 1 && IS_VOID(++val) && zombie == len) {
                zombie = hash;
            }
            hash += skip;
            if (hash >= len) hash -= len;
        }
    }
    else if (ANY_BINSTR(key)) {
        while ((n = hashes[hash])) {
            val = ARR_AT(array, (n - 1) * wide);
            if (VAL_TYPE(val) == VAL_TYPE(key)) {
                if (0 == Compare_String_Vals(val, key, FALSE)) return hash;
                if (
                    !cased && uncased == len
                    && 0 == Compare_String_Vals(
                        val, key, LOGICAL(!IS_BINARY(key))
                    )
                ) {
                    uncased = hash;
                }
            }
            if (wide > 1 && IS_VOID(++val) && zombie == len)  {
                zombie = hash;
            }
            hash += skip;
            if (hash >= len) hash -= len;<commit_msg>Don't try to take VAL_WORD_CANON() on non-words in uncased matching<commit_after>//
//  File: %t-map.c
//  Summary: "map datatype"
//  Section: datatypes
//  Project: "Rebol 3 Interpreter and Run-time (Ren-C branch)"
//  Homepage: https://github.com/metaeducation/ren-c/
//
//=////////////////////////////////////////////////////////////////////////=//
//
// Copyright 2012 REBOL Technologies
// Copyright 2012-2017 Rebol Open Source Contributors
// REBOL is a trademark of REBOL Technologies
//
// See README.md and CREDITS.md for more information.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//=////////////////////////////////////////////////////////////////////////=//
//
// See %sys-map.h for an explanation of the map structure.
//

#include "sys-core.h"

//
//  CT_Map: C
//
REBINT CT_Map(const RELVAL *a, const RELVAL *b, REBINT mode)
{
    if (mode < 0) return -1;
    return 0 == Cmp_Array(a, b, FALSE);
}


//
//  Make_Map: C
//
// Makes a MAP block (that holds both keys and values).
// Capacity is measured in key-value pairings.
// A hash series is also created.
//
static REBMAP *Make_Map(REBCNT capacity)
{
    REBARR *pairlist = Make_Array_Core(capacity * 2, ARRAY_FLAG_PAIRLIST);
    SER(pairlist)->link.hashlist = Make_Hash_Sequence(capacity);

    return MAP(pairlist);
}


//
//  Find_Key_Hashed: C
//
// Returns hash index (either the match or the new one).
// A return of zero is valid (as a hash index);
//
// Wide: width of record (normally 2, a key and a value).
//
// Modes:
//     0 - search, return hash if found or not
//     1 - search, return hash, else return -1 if not
//     2 - search, return hash, else append value and return -1
//
REBINT Find_Key_Hashed(
    REBARR *array,
    REBSER *hashlist,
    const RELVAL *key, // !!! assumes key is followed by value(s) via ++
    REBSPC *specifier,
    REBCNT wide,
    REBOOL cased,
    REBYTE mode
) {
    REBCNT len = SER_LEN(hashlist);
    assert(len > 0);

    REBCNT hash = Hash_Value(key);

    // The REBCNT[] hash array size is chosen to try and make a large enough
    // table relative to the data that collisions will be hopefully not
    // frequent.  But they may still collide.  The method R3-Alpha chose to
    // deal with collisions was to have a "skip" amount that will go try
    // another hash bucket until the searched for key is found or a 0
    // entry in the hashlist is found.
    //
    // Note: if len and skip are co-primes is guaranteed that repeatedly
    // adding skip (and subtracting len when needed) all positions are
    // visited.  1 <= skip < len, and len is prime, so this is guaranteed.

    REBCNT skip = hash % (len - 1) + 1;

    hash = hash % len;

    // a 'zombie' is a key with void value, that may be overwritten.  Set to
    // len to indicate zombie not yet encountered.
    //
    REBCNT zombie = len;

    REBCNT uncased = len; // uncased match not yet encountered

    // Scan hash table for match:

    REBCNT *hashes = SER_HEAD(REBCNT, hashlist);
    REBCNT n;
    RELVAL *val;

    if (ANY_WORD(key)) {
        while ((n = hashes[hash])) {
            val = ARR_AT(array, (n - 1) * wide);
            if (ANY_WORD(val)) {
                if (VAL_WORD_SPELLING(key) == VAL_WORD_SPELLING(val))
                    return hash; // exact match

                if (NOT(cased) && uncased == len) // not cased w/no match yet
                    if (VAL_WORD_CANON(key) == VAL_WORD_CANON(val))
                        uncased = hash; // indicate uncased match found
            }
            else if (wide > 1 && IS_VOID(++val) && zombie == len) {
                zombie = hash;
            }
            hash += skip;
            if (hash >= len) hash -= len;
        }
    }
    else if (ANY_BINSTR(key)) {
        while ((n = hashes[hash])) {
            val = ARR_AT(array, (n - 1) * wide);
            if (VAL_TYPE(val) == VAL_TYPE(key)) {
                if (0 == Compare_String_Vals(val, key, FALSE)) return hash;
                if (
                    !cased && uncased == len
                    && 0 == Compare_String_Vals(
                        val, key, LOGICAL(!IS_BINARY(key))
                    )
                ) {
                    uncased = hash;
                }
            }
            if (wide > 1 && IS_VOID(++val) && zombie == len)  {
                zombie = hash;
            }
            hash += skip;
            if (hash >= len) hash -= len;
{'fraction_non_alphanumeric': 0.14313786327438205, 'fraction_numerical': 0.008067018305926156, 'mean_word_length': 1.8391074574280681, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 15, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3257459', 'n_tokens_mistral': 3029, 'n_tokens_neox': 2871, 'n_words': 1069}