/* Node in soundex code tree */ typedefstruct dm_node
{ int soundex_length; /* Length of generated soundex code */ char soundex[DM_CODE_DIGITS]; /* Soundex code */ int is_leaf; /* Candidate for complete soundex code */ int last_update; /* Letter number for last update of node */ char code_digit; /* Last code digit, 0 - 9 */
/* *Oneortwoalternatecodedigitsleadingtothisnode.Iftherearetwo *digits,oneofthemisalwaysan'X'.Repeatedcodedigitsand'X'lead *backtothesamenode.
*/ char prev_code_digits[2]; /* One or two alternate code digits moving forward. */ char next_code_digits[2]; /* ORed together code index(es) used to reach current node. */ int prev_code_index; int next_code_index; /* Possible nodes branching out from this node - digits 0-9. */ struct dm_node *children[10]; /* Next node in linked list. Alternating index for each iteration. */ struct dm_node *next[2];
} dm_node;
/* Dummy soundex codes at end of input. */ staticconst dm_codes end_codes[2] =
{
{ "X", "X", "X"
}
};
/* Mapping from ISO8859-1 to upper-case ASCII, covering the range 0x60..0xFF. */ staticconstchar iso8859_1_to_ascii_upper[] = "`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~ ! ?AAAAAAECEEEEIIIIDNOOOOO*OUUUUYDSAAAAAAECEEEEIIIIDNOOOOO/OUUUUYDY";
/* Internal C implementation */ staticbool daitch_mokotoff_coding(constchar *word, ArrayBuildState *soundex);
PG_FUNCTION_INFO_V1(daitch_mokotoff);
Datum
daitch_mokotoff(PG_FUNCTION_ARGS)
{
text *arg = PG_GETARG_TEXT_PP(0);
Datum retval; char *string;
ArrayBuildState *soundex;
MemoryContext old_ctx,
tmp_ctx;
/* Work in a temporary context to simplify cleanup. */
tmp_ctx = AllocSetContextCreate(CurrentMemoryContext, "daitch_mokotoff temporary context",
ALLOCSET_DEFAULT_SIZES);
old_ctx = MemoryContextSwitchTo(tmp_ctx);
/* We must convert the string to UTF-8 if it isn't already. */
string = pg_server_to_any(text_to_cstring(arg), VARSIZE_ANY_EXHDR(arg),
PG_UTF8);
/* The result is built in this ArrayBuildState. */
soundex = initArrayResult(TEXTOID, tmp_ctx, false);
if (!daitch_mokotoff_coding(string, soundex))
{ /* No encodable characters in input */
MemoryContextSwitchTo(old_ctx);
MemoryContextDelete(tmp_ctx);
PG_RETURN_NULL();
}
/* Initialize soundex code tree node for next code digit. */ staticvoid
initialize_node(dm_node *node, int last_update)
{ if (node->last_update < last_update)
{
node->prev_code_digits[0] = node->next_code_digits[0];
node->prev_code_digits[1] = node->next_code_digits[1];
node->next_code_digits[0] = '\0';
node->next_code_digits[1] = '\0';
node->prev_code_index = node->next_code_index;
node->next_code_index = 0;
node->is_leaf = 0;
node->last_update = last_update;
}
}
/* Update soundex code tree node with next code digit. */ staticvoid
add_next_code_digit(dm_node *node, int code_index, char code_digit)
{ /* OR in index 1 or 2. */
node->next_code_index |= code_index;
/* Mark soundex code tree node as leaf. */ staticvoid
set_leaf(dm_node *first_node[2], dm_node *last_node[2],
dm_node *node, int ix_node)
{ if (!node->is_leaf)
{
node->is_leaf = 1;
/* Update node for next code digit(s). */ staticvoid
update_node(dm_node *first_node[2], dm_node *last_node[2],
dm_node *node, int ix_node, int letter_no, int prev_code_index, int next_code_index, constchar *next_code_digits, int digit_no,
ArrayBuildState *soundex)
{ int i; char next_code_digit = next_code_digits[digit_no]; int num_dirty_nodes = 0;
dm_node *dirty_nodes[2];
if (next_code_digit == 'X' ||
(digit_no == 0 &&
(node->prev_code_digits[0] == next_code_digit ||
node->prev_code_digits[1] == next_code_digit)))
{ /* The code digit is the same as one of the previous (i.e. not added). */
dirty_nodes[num_dirty_nodes++] = node;
}
if (next_code_digit != 'X' &&
(digit_no > 0 ||
node->prev_code_digits[0] != next_code_digit ||
node->prev_code_digits[1]))
{ /* The code digit is different from one of the previous (i.e. added). */
node = find_or_create_child_node(node, next_code_digit, soundex); if (node)
{
initialize_node(node, letter_no);
dirty_nodes[num_dirty_nodes++] = node;
}
}
for (i = 0; i < num_dirty_nodes; i++)
{ /* Add code digit leading to the current node. */
add_next_code_digit(dirty_nodes[i], next_code_index, next_code_digit);
/* Update soundex tree leaf nodes. */ staticvoid
update_leaves(dm_node *first_node[2], int *ix_node, int letter_no, const dm_codes *codes, const dm_codes *next_codes,
ArrayBuildState *soundex)
{ int i,
j,
code_index;
dm_node *node,
*last_node[2]; const dm_code *code,
*next_code; int ix_node_next = (*ix_node + 1) & 1; /* Alternating index: 0, 1 */
/* Initialize for new linked list of leaves. */
first_node[ix_node_next] = NULL;
last_node[ix_node_next] = NULL;
/* Process all nodes. */ for (node = first_node[*ix_node]; node; node = node->next[*ix_node])
{ /* One or two alternate code sequences. */ for (i = 0; i < 2 && (code = codes[i]) && code[0][0]; i++)
{ /* Coding for previous letter - before vowel: 1, all other: 2 */ int prev_code_index = (code[0][0] > '1') + 1;
/* One or two alternate next code sequences. */ for (j = 0; j < 2 && (next_code = next_codes[j]) && next_code[0][0]; j++)
{ /* Determine which code to use. */ if (letter_no == 0)
{ /* This is the first letter. */
code_index = 0;
} elseif (next_code[0][0] <= '1')
{ /* The next letter is a vowel. */
code_index = 1;
} else
{ /* All other cases. */
code_index = 2;
}
/* One or two sequential code digits. */
update_node(first_node, last_node, node, ix_node_next,
letter_no, prev_code_index, code_index,
code[code_index], 0,
soundex);
}
}
}
*ix_node = ix_node_next;
}
/* *Returnnextcharacter,convertedfromUTF-8touppercaseASCII. **ixisthecurrentstringindexandisincrementedbythecharacterlength.
*/ staticchar
read_char(constunsignedchar *str, int *ix)
{ /* Substitute character for skipped code points. */ constchar na = '\x1a';
pg_wchar c;
/* Decode UTF-8 character to ISO 10646 code point. */
str += *ix;
c = utf8_to_unicode(str);
/* Advance *ix, but (for safety) not if we've reached end of string. */ if (c)
*ix += pg_utf_mblen(str);
/* Convert. */ if (c >= (unsignedchar) '[' && c <= (unsignedchar) ']')
{ /* ASCII characters [, \, and ] are reserved for conversions below. */ return na;
} elseif (c < 0x60)
{ /* Other non-lowercase ASCII characters can be used as-is. */ return (char) c;
} elseif (c < 0x100)
{ /* ISO-8859-1 code point; convert to upper-case ASCII via table. */ return iso8859_1_to_ascii_upper[c - 0x60];
} else
{ /* Conversion of non-ASCII characters in the coding chart. */ switch (c)
{ case0x0104: /* LATIN CAPITAL LETTER A WITH OGONEK */ case0x0105: /* LATIN SMALL LETTER A WITH OGONEK */ return'['; case0x0118: /* LATIN CAPITAL LETTER E WITH OGONEK */ case0x0119: /* LATIN SMALL LETTER E WITH OGONEK */ return'\\'; case0x0162: /* LATIN CAPITAL LETTER T WITH CEDILLA */ case0x0163: /* LATIN SMALL LETTER T WITH CEDILLA */ case0x021A: /* LATIN CAPITAL LETTER T WITH COMMA BELOW */ case0x021B: /* LATIN SMALL LETTER T WITH COMMA BELOW */ return']'; default: return na;
}
}
}
/* Read next ASCII character, skipping any characters not in [A-\]]. */ staticchar
read_valid_char(constchar *str, int *ix)
{ char c;
while ((c = read_char((constunsignedchar *) str, ix)) != '\0')
{ if (c >= 'A' && c <= ']') break;
}
return c;
}
/* Return sound coding for "letter" (letter sequence) */ staticconst dm_codes *
read_letter(constchar *str, int *ix)
{ char c,
cmp; int i,
j; const dm_letter *letters; const dm_codes *codes;
/* First letter in sequence. */ if ((c = read_valid_char(str, ix)) == '\0') return NULL;
/* Any subsequent letters in sequence. */ while ((letters = letters->letters) && (c = read_valid_char(str, &i)))
{ for (j = 0; (cmp = letters[j].letter); j++)
{ if (cmp == c)
{ /* Letter found. */
letters = &letters[j]; if (letters->codes)
{ /* Coding for letter sequence found. */
codes = letters->codes;
*ix = i;
} break;
}
} if (!cmp)
{ /* The sequence of letters has no coding. */ break;
}
}
return codes;
}
/* *GenerateallDaitch-Mokotoffsoundexcodesforword, *addingthemtothe"soundex"ArrayBuildState. *Returnsfalseifstringhasnoencodablecharacters,elsetrue.
*/ staticbool
daitch_mokotoff_coding(constchar *word, ArrayBuildState *soundex)
{ int i = 0; int letter_no = 0; int ix_node = 0; const dm_codes *codes,
*next_codes;
dm_node *first_node[2],
*node;
/* First letter. */ if (!(codes = read_letter(word, &i)))
{ /* No encodable character in input. */ returnfalse;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.