regex: Perform char decomposition on C++ side
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
#include <cstring>
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
#include "KString.h"
|
#include "KString.h"
|
||||||
#include "Natives.h"
|
#include "Natives.h"
|
||||||
@@ -560,21 +561,64 @@ KBoolean Kotlin_text_regex_hasSingleCodepointDecompositionInternal(KInt ch) {
|
|||||||
return singleDecompositions[index] == ch;
|
return singleDecompositions[index] == ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
OBJ_GETTER(Kotlin_text_getDecompositionInternal, KInt ch) {
|
const Decomposition* getDecomposition(KInt codePoint) {
|
||||||
// TODO: Move all uses of decomposition table into C++ code.
|
int index = binarySearchRange(decompositionKeys, ARRAY_SIZE(decompositionKeys), codePoint);
|
||||||
int index = binarySearchRange(decompositionKeys, ARRAY_SIZE(decompositionKeys), ch);
|
if (decompositionKeys[index] != codePoint) {
|
||||||
if (decompositionKeys[index] != ch) {
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
const Decomposition& decomposition = decompositionValues[index];
|
return &decompositionValues[index];
|
||||||
ArrayHeader* result = AllocArrayInstance(theIntArrayTypeInfo, decomposition.length, OBJ_RESULT)->array();
|
}
|
||||||
|
|
||||||
|
OBJ_GETTER(Kotlin_text_regex_getDecompositionInternal, KInt ch) {
|
||||||
|
const Decomposition* decomposition = getDecomposition(ch);
|
||||||
|
if (decomposition == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
ArrayHeader* result = AllocArrayInstance(theIntArrayTypeInfo, decomposition->length, OBJ_RESULT)->array();
|
||||||
KInt* resultRaw = IntArrayAddressOfElementAt(result, 0);
|
KInt* resultRaw = IntArrayAddressOfElementAt(result, 0);
|
||||||
for (int i = 0; i < decomposition.length; i++) {
|
for (int i = 0; i < decomposition->length; i++) {
|
||||||
*resultRaw++ = decomposition.array[i];
|
*resultRaw++ = decomposition->array[i];
|
||||||
}
|
}
|
||||||
RETURN_OBJ(result->obj());
|
RETURN_OBJ(result->obj());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KInt Kotlin_text_regex_decomposeString(ArrayHeader* inputCodePoints, KInt inputLength, ArrayHeader* outputCodePoints) {
|
||||||
|
RuntimeAssert(inputCodePoints->type_info() == theIntArrayTypeInfo, "Must use an Int array");
|
||||||
|
RuntimeAssert(outputCodePoints->type_info() == theIntArrayTypeInfo, "Must use an Int array");
|
||||||
|
RuntimeAssert(inputLength >= 0, "Input length must be >= 0");
|
||||||
|
if (inputLength == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int outputLength = 0;
|
||||||
|
const KInt* inputArray = IntArrayAddressOfElementAt(inputCodePoints, 0);
|
||||||
|
KInt* outputArray = IntArrayAddressOfElementAt(outputCodePoints, 0);
|
||||||
|
for (int i = 0; i < inputLength; i++) {
|
||||||
|
const Decomposition* decomposition = getDecomposition(inputArray[i]);
|
||||||
|
if (decomposition == nullptr) {
|
||||||
|
outputArray[outputLength++] = inputArray[i];
|
||||||
|
} else {
|
||||||
|
memcpy(outputArray + outputLength, decomposition->array, decomposition->length * sizeof(KInt));
|
||||||
|
outputLength+=decomposition->length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outputLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
KInt Kotlin_text_regex_decomposeCodePoint(KInt codePoint, ArrayHeader* outputCodePoints, KInt fromIndex) {
|
||||||
|
RuntimeAssert(outputCodePoints->type_info() == theIntArrayTypeInfo, "Must be an Int array");
|
||||||
|
RuntimeAssert(fromIndex >= 0 && fromIndex < outputCodePoints->count_, "Start index must be >= 0 and < array size");
|
||||||
|
KInt* rawResult = IntArrayAddressOfElementAt(outputCodePoints, fromIndex);
|
||||||
|
const Decomposition* decomposition = getDecomposition(codePoint);
|
||||||
|
if (decomposition == nullptr) {
|
||||||
|
*rawResult = codePoint;
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
memcpy(rawResult, decomposition->array, decomposition->length * sizeof(KInt));
|
||||||
|
return decomposition->length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,6 @@ class Regex internal constructor(internal val nativePattern: Pattern) {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/** Returns a literal regex for the specified [literal] string. */
|
/** Returns a literal regex for the specified [literal] string. */
|
||||||
// TODO: Uncomment for native
|
|
||||||
fun fromLiteral(literal: String): Regex = Regex(literal, RegexOption.LITERAL)
|
fun fromLiteral(literal: String): Regex = Regex(literal, RegexOption.LITERAL)
|
||||||
|
|
||||||
/** Returns a literal pattern for the specified [literal] string. */
|
/** Returns a literal pattern for the specified [literal] string. */
|
||||||
@@ -159,7 +158,6 @@ class Regex internal constructor(internal val nativePattern: Pattern) {
|
|||||||
infix fun matches(input: CharSequence): Boolean = doMatch(input, Mode.MATCH) != null
|
infix fun matches(input: CharSequence): Boolean = doMatch(input, Mode.MATCH) != null
|
||||||
|
|
||||||
/** Indicates whether the regular expression can find at least one match in the specified [input]. */
|
/** Indicates whether the regular expression can find at least one match in the specified [input]. */
|
||||||
// TODO: Looks like we don't need Mode anymore.
|
|
||||||
fun containsMatchIn(input: CharSequence): Boolean = find(input) != null
|
fun containsMatchIn(input: CharSequence): Boolean = find(input) != null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -172,7 +170,6 @@ class Regex internal constructor(internal val nativePattern: Pattern) {
|
|||||||
if (startIndex < 0 || startIndex > input.length) {
|
if (startIndex < 0 || startIndex > input.length) {
|
||||||
throw IndexOutOfBoundsException() // TODO: Add a message.
|
throw IndexOutOfBoundsException() // TODO: Add a message.
|
||||||
}
|
}
|
||||||
// TODO: reuse the match result?
|
|
||||||
val matchResult = MatchResultImpl(input, this)
|
val matchResult = MatchResultImpl(input, this)
|
||||||
matchResult.mode = Mode.FIND
|
matchResult.mode = Mode.FIND
|
||||||
matchResult.startIndex = startIndex
|
matchResult.startIndex = startIndex
|
||||||
@@ -181,14 +178,13 @@ class Regex internal constructor(internal val nativePattern: Pattern) {
|
|||||||
matchResult.finalizeMatch()
|
matchResult.finalizeMatch()
|
||||||
return matchResult
|
return matchResult
|
||||||
} else {
|
} else {
|
||||||
/*matchResult.hitEnd = true
|
|
||||||
matchResult.startIndex = -1*/
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a sequence of all occurrences of a regular expression within the [input] string, beginning at the specified [startIndex].
|
* Returns a sequence of all occurrences of a regular expression within the [input] string,
|
||||||
|
* beginning at the specified [startIndex].
|
||||||
*/
|
*/
|
||||||
fun findAll(input: CharSequence, startIndex: Int = 0): Sequence<MatchResult>
|
fun findAll(input: CharSequence, startIndex: Int = 0): Sequence<MatchResult>
|
||||||
= generateSequence({ find(input, startIndex) }, MatchResult::next)
|
= generateSequence({ find(input, startIndex) }, MatchResult::next)
|
||||||
@@ -233,9 +229,10 @@ class Regex internal constructor(internal val nativePattern: Pattern) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces all occurrences of this regular expression in the specified [input] string with specified [replacement] expression.
|
* Replaces all occurrences of this regular expression in the specified [input] string with
|
||||||
|
* specified [replacement] expression.
|
||||||
*
|
*
|
||||||
* @param replacement A replacement expression that can include substitutions. See [Matcher.appendReplacement] for details.
|
* @param replacement A replacement expression that can include substitutions.
|
||||||
*/
|
*/
|
||||||
fun replace(input: CharSequence, replacement: String): String
|
fun replace(input: CharSequence, replacement: String): String
|
||||||
= replace(input) { match -> processReplacement(match, replacement) }
|
= replace(input) { match -> processReplacement(match, replacement) }
|
||||||
@@ -280,7 +277,6 @@ class Regex internal constructor(internal val nativePattern: Pattern) {
|
|||||||
* @param limit Non-negative value specifying the maximum number of substrings the string can be split to.
|
* @param limit Non-negative value specifying the maximum number of substrings the string can be split to.
|
||||||
* Zero by default means no limit is set.
|
* Zero by default means no limit is set.
|
||||||
*/
|
*/
|
||||||
// TODO: replace all argument checks with require function.
|
|
||||||
fun split(input: CharSequence, limit: Int = 0): List<String> {
|
fun split(input: CharSequence, limit: Int = 0): List<String> {
|
||||||
require(limit >= 0, { "Limit must be non-negative, but was $limit." } )
|
require(limit >= 0, { "Limit must be non-negative, but was $limit." } )
|
||||||
if (input.isEmpty()) {
|
if (input.isEmpty()) {
|
||||||
|
|||||||
@@ -671,7 +671,7 @@ public inline fun CharSequence.replaceFirst(regex: Regex, replacement: String):
|
|||||||
* Returns `true` if this char sequence matches the given regular expression.
|
* Returns `true` if this char sequence matches the given regular expression.
|
||||||
*/
|
*/
|
||||||
@kotlin.internal.InlineOnly
|
@kotlin.internal.InlineOnly
|
||||||
public inline fun CharSequence.matches(regex: Regex): Boolean = regex.matches(this)
|
public infix inline fun CharSequence.matches(regex: Regex): Boolean = regex.matches(this)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of [regionMatches] for CharSequences.
|
* Implementation of [regionMatches] for CharSequences.
|
||||||
|
|||||||
@@ -28,9 +28,15 @@ external private fun getCanonicalClassInternal(ch: Int): Int
|
|||||||
external private fun hasSingleCodepointDecompositionInternal(ch: Int): Boolean
|
external private fun hasSingleCodepointDecompositionInternal(ch: Int): Boolean
|
||||||
|
|
||||||
/** Returns a decomposition for a given codepoint. */
|
/** Returns a decomposition for a given codepoint. */
|
||||||
@SymbolName("Kotlin_text_getDecompositionInternal")
|
@SymbolName("Kotlin_text_regex_getDecompositionInternal")
|
||||||
external private fun getDecompositionInternal(ch: Int): IntArray?
|
external private fun getDecompositionInternal(ch: Int): IntArray?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decomposes the given string represented as an array of codepoints. Saves the decomposition into [outputCodepoints] array.
|
||||||
|
* Returns the length of the decomposition.
|
||||||
|
*/
|
||||||
|
@SymbolName("Kotlin_text_regex_decomposeString")
|
||||||
|
external private fun decomposeString(inputCodePoints: IntArray, inputLength: Int, outputCodePoints: IntArray): Int
|
||||||
// =============================================================================================================
|
// =============================================================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -792,13 +798,8 @@ internal class Lexer(val patternString: String, flags: Int) {
|
|||||||
// =============================================================================================================
|
// =============================================================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize given expression.
|
* Normalize given string.
|
||||||
|
|
||||||
* @param input - expression to normalize
|
|
||||||
* *
|
|
||||||
* @return normalized expression.
|
|
||||||
*/
|
*/
|
||||||
// TODO: Implement on the C++ side.
|
|
||||||
fun normalize(input: String): String {
|
fun normalize(input: String): String {
|
||||||
val inputChars = input.toCharArray()
|
val inputChars = input.toCharArray()
|
||||||
val inputLength = inputChars.size
|
val inputLength = inputChars.size
|
||||||
@@ -824,45 +825,22 @@ internal class Lexer(val patternString: String, flags: Int) {
|
|||||||
//result of canonical decomposition of input in UTF-16 encoding
|
//result of canonical decomposition of input in UTF-16 encoding
|
||||||
val result = StringBuilder()
|
val result = StringBuilder()
|
||||||
|
|
||||||
run {
|
var i = 0
|
||||||
var i = 0
|
while (i < inputLength) {
|
||||||
while (i < inputLength) {
|
ch = input.codePointAt(i)
|
||||||
ch = input.codePointAt(i)
|
inputCodePoints[inputCodePointsIndex++] = ch
|
||||||
inputCodePoints[inputCodePointsIndex++] = ch
|
i += if (Char.isSupplementaryCodePoint(ch)) 2 else 1
|
||||||
i += if (Char.isSupplementaryCodePoint(ch)) 2 else 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Canonical decomposition based on mappings in decomposition table.
|
||||||
* Canonical decomposition based on mappings in decompTable
|
resCodePointsIndex = decomposeString(inputCodePoints, inputCodePointsIndex, resCodePoints)
|
||||||
*/
|
|
||||||
for (i in 0..inputCodePointsIndex - 1) {
|
|
||||||
ch = inputCodePoints[i]
|
|
||||||
|
|
||||||
decomp = Lexer.getDecomposition(ch)
|
// Canonical ordering.
|
||||||
if (decomp == null) {
|
// See http://www.unicode.org/reports/tr15/#Decomposition for details
|
||||||
resCodePoints[resCodePointsIndex++] = ch
|
|
||||||
} else {
|
|
||||||
val curSymbDecompLength = decomp.size
|
|
||||||
|
|
||||||
for (j in 0..curSymbDecompLength - 1) {
|
|
||||||
resCodePoints[resCodePointsIndex++] = decomp[j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Canonical ordering.
|
|
||||||
* See http://www.unicode.org/reports/tr15/#Decomposition for
|
|
||||||
* details
|
|
||||||
*/
|
|
||||||
resCodePoints = Lexer.getCanonicalOrder(resCodePoints, resCodePointsIndex)
|
resCodePoints = Lexer.getCanonicalOrder(resCodePoints, resCodePointsIndex)
|
||||||
|
|
||||||
/*
|
// Decomposition for Hangul syllables.
|
||||||
* Decomposition for Hangul syllables.
|
// See http://www.unicode.org/reports/tr15/#Hangul for details
|
||||||
* See http://www.unicode.org/reports/tr15/#Hangul for
|
|
||||||
* details
|
|
||||||
*/
|
|
||||||
decompHangul = IntArray(resCodePoints.size)
|
decompHangul = IntArray(resCodePoints.size)
|
||||||
|
|
||||||
for (i in 0..resCodePointsIndex - 1) {
|
for (i in 0..resCodePointsIndex - 1) {
|
||||||
@@ -872,11 +850,7 @@ internal class Lexer(val patternString: String, flags: Int) {
|
|||||||
if (decomp == null) {
|
if (decomp == null) {
|
||||||
decompHangul[decompHangulIndex++] = curSymb
|
decompHangul[decompHangulIndex++] = curSymb
|
||||||
} else {
|
} else {
|
||||||
|
// Note that Hangul decompositions have length that is equal 2 or 3.
|
||||||
/*
|
|
||||||
* Note that Hangul decompositions have length that is
|
|
||||||
* equal 2 or 3.
|
|
||||||
*/
|
|
||||||
decompHangul[decompHangulIndex++] = decomp[0]
|
decompHangul[decompHangulIndex++] = decomp[0]
|
||||||
decompHangul[decompHangulIndex++] = decomp[1]
|
decompHangul[decompHangulIndex++] = decomp[1]
|
||||||
if (decomp.size == 3) {
|
if (decomp.size == 3) {
|
||||||
@@ -885,9 +859,7 @@ internal class Lexer(val patternString: String, flags: Int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Translating into UTF-16 encoding
|
||||||
* Translating into UTF-16 encoding
|
|
||||||
*/
|
|
||||||
for (i in 0..decompHangulIndex - 1) {
|
for (i in 0..decompHangulIndex - 1) {
|
||||||
result.append(Char.toChars(decompHangul[i]))
|
result.append(Char.toChars(decompHangul[i]))
|
||||||
}
|
}
|
||||||
@@ -896,17 +868,8 @@ internal class Lexer(val patternString: String, flags: Int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rearrange codepoints according
|
* Rearrange codepoints in [inputInts] according to canonical order. Return an array with rearranged codepoints.
|
||||||
* to canonical order.
|
|
||||||
|
|
||||||
* @param inputInts - array that contains Unicode codepoints
|
|
||||||
* *
|
|
||||||
* @param length - index of last Unicode codepoint plus 1
|
|
||||||
* *
|
|
||||||
* *
|
|
||||||
* @return array that contains rearranged codepoints.
|
|
||||||
*/
|
*/
|
||||||
// TODO: C++ side!
|
|
||||||
fun getCanonicalOrder(inputInts: IntArray, length: Int): IntArray {
|
fun getCanonicalOrder(inputInts: IntArray, length: Int): IntArray {
|
||||||
val inputLength = if (length < inputInts.size)
|
val inputLength = if (length < inputInts.size)
|
||||||
length
|
length
|
||||||
@@ -914,12 +877,9 @@ internal class Lexer(val patternString: String, flags: Int) {
|
|||||||
inputInts.size
|
inputInts.size
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Simple bubble-sort algorithm.
|
* Simple bubble-sort algorithm. Note that many codepoints have 0 canonical class, so this algorithm works
|
||||||
* Note that many codepoints have 0
|
* almost lineary in overwhelming majority of cases. This is due to specific of Unicode combining
|
||||||
* canonical class, so this algorithm works
|
* classes and codepoints.
|
||||||
* almost lineary in overwhelming majority
|
|
||||||
* of cases. This is due to specific of Unicode
|
|
||||||
* combining classes and codepoints.
|
|
||||||
*/
|
*/
|
||||||
for (i in 1..inputLength - 1) {
|
for (i in 1..inputLength - 1) {
|
||||||
var j = i - 1
|
var j = i - 1
|
||||||
@@ -951,14 +911,8 @@ internal class Lexer(val patternString: String, flags: Int) {
|
|||||||
/**
|
/**
|
||||||
* Gets decomposition for given Hangul syllable.
|
* Gets decomposition for given Hangul syllable.
|
||||||
* This is an implementation of Hangul decomposition algorithm
|
* This is an implementation of Hangul decomposition algorithm
|
||||||
* according to http://www.unicode.org/versions/Unicode4.0.0/ch03.pdf
|
* according to http://www.unicode.org/versions/Unicode4.0.0/ch03.pdf "3.12 Conjoining Jamo Behavior".
|
||||||
* "3.12 Conjoining Jamo Behavior".
|
|
||||||
|
|
||||||
* @param ch - given Hangul syllable
|
|
||||||
* *
|
|
||||||
* @return canonical decomposition of currentChar.
|
|
||||||
*/
|
*/
|
||||||
// TODO: C++
|
|
||||||
fun getHangulDecomposition(ch: Int): IntArray? {
|
fun getHangulDecomposition(ch: Int): IntArray? {
|
||||||
val SIndex = ch - SBase
|
val SIndex = ch - SBase
|
||||||
|
|
||||||
|
|||||||
@@ -116,13 +116,13 @@ constructor (internal val input: CharSequence,
|
|||||||
|
|
||||||
override fun next(): MatchResult? {
|
override fun next(): MatchResult? {
|
||||||
var nextStart = range.endInclusive + 1
|
var nextStart = range.endInclusive + 1
|
||||||
if (nextStart == input.length) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
// If the current match is empty - shift by 1.
|
// If the current match is empty - shift by 1.
|
||||||
if (nextStart == range.start) {
|
if (nextStart == range.start) {
|
||||||
nextStart++
|
nextStart++
|
||||||
}
|
}
|
||||||
|
if (nextStart > input.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
return regex.find(input, nextStart)
|
return regex.find(input, nextStart)
|
||||||
}
|
}
|
||||||
// =================================================================================================================
|
// =================================================================================================================
|
||||||
|
|||||||
@@ -17,6 +17,13 @@
|
|||||||
|
|
||||||
package kotlin.text.regex
|
package kotlin.text.regex
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decomposes the given codepoint. Saves the decomposition into [outputCodepoints] array starting with [fromIndex].
|
||||||
|
* Returns the length of the decomposition.
|
||||||
|
*/
|
||||||
|
@SymbolName("Kotlin_text_regex_decomposeCodePoint")
|
||||||
|
external private fun decomposeCodePoint(codePoint: Int, outputCodePoints: IntArray, fromIndex: Int): Int
|
||||||
|
|
||||||
/** Represents canonical decomposition of Unicode character. Is used when CANON_EQ flag of Pattern class is specified. */
|
/** Represents canonical decomposition of Unicode character. Is used when CANON_EQ flag of Pattern class is specified. */
|
||||||
open internal class DecomposedCharSet(
|
open internal class DecomposedCharSet(
|
||||||
/** Decomposition of the Unicode codepoint */
|
/** Decomposition of the Unicode codepoint */
|
||||||
@@ -48,19 +55,11 @@ open internal class DecomposedCharSet(
|
|||||||
// We read testString and decompose it gradually to compare with this decomposedChar at position strIndex
|
// We read testString and decompose it gradually to compare with this decomposedChar at position strIndex
|
||||||
var curChar = codePointAt(strIndex, testString, rightBound)
|
var curChar = codePointAt(strIndex, testString, rightBound)
|
||||||
strIndex += readCharsForCodePoint
|
strIndex += readCharsForCodePoint
|
||||||
var decomposedCurrentCodePoint: IntArray? = Lexer.getDecomposition(curChar)
|
|
||||||
var readCodePoints = 0
|
var readCodePoints = 0
|
||||||
var i = 0
|
var i = 0
|
||||||
// All decompositions have length that is less or equal Lexer.MAX_DECOMPOSITION_LENGTH
|
// All decompositions have length that is less or equal Lexer.MAX_DECOMPOSITION_LENGTH
|
||||||
var decomposedCodePoint: IntArray
|
var decomposedCodePoint: IntArray = IntArray(Lexer.MAX_DECOMPOSITION_LENGTH)
|
||||||
if (decomposedCurrentCodePoint == null) {
|
readCodePoints += decomposeCodePoint(curChar, decomposedCodePoint, readCodePoints)
|
||||||
decomposedCodePoint = IntArray(Lexer.MAX_DECOMPOSITION_LENGTH)
|
|
||||||
decomposedCodePoint[readCodePoints++] = curChar
|
|
||||||
} else {
|
|
||||||
i = decomposedCurrentCodePoint.size
|
|
||||||
decomposedCodePoint = decomposedCurrentCodePoint.copyOf(Lexer.MAX_DECOMPOSITION_LENGTH)
|
|
||||||
readCodePoints += i
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strIndex < rightBound) {
|
if (strIndex < rightBound) {
|
||||||
curChar = codePointAt(strIndex, testString, rightBound)
|
curChar = codePointAt(strIndex, testString, rightBound)
|
||||||
@@ -71,26 +70,11 @@ open internal class DecomposedCharSet(
|
|||||||
if (!Lexer.hasDecompositionNonNullCanClass(curChar)) {
|
if (!Lexer.hasDecompositionNonNullCanClass(curChar)) {
|
||||||
decomposedCodePoint[readCodePoints++] = curChar
|
decomposedCodePoint[readCodePoints++] = curChar
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A few codepoints have decompositions and non null
|
* A few codepoints have decompositions and non null canonical classes, we have to take them into
|
||||||
* canonical classes, we have to take them into
|
* consideration, but general rule is: if canonical class != 0 then no decomposition
|
||||||
* consideration, but general rule is:
|
|
||||||
* if canonical class != 0 then no decomposition
|
|
||||||
*/
|
*/
|
||||||
decomposedCurrentCodePoint = Lexer.getDecomposition(curChar)
|
readCodePoints += decomposeCodePoint(curChar, decomposedCodePoint, readCodePoints)
|
||||||
|
|
||||||
/*
|
|
||||||
* Length of such decomposition is 1 or 2. See UnicodeData file
|
|
||||||
* http://www.unicode.org/Public/4.0-Update/UnicodeData-4.0.0.txt
|
|
||||||
*/
|
|
||||||
// hasDecompositionNonNullCanClass(curChar) == true, so decomposedCurrentCodePoint != null.
|
|
||||||
if (decomposedCurrentCodePoint!!.size == 2) {
|
|
||||||
decomposedCodePoint[readCodePoints++] = decomposedCurrentCodePoint[0]
|
|
||||||
decomposedCodePoint[readCodePoints++] = decomposedCurrentCodePoint[1]
|
|
||||||
} else {
|
|
||||||
decomposedCodePoint[readCodePoints++] = decomposedCurrentCodePoint[0]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
strIndex += readCharsForCodePoint
|
strIndex += readCharsForCodePoint
|
||||||
@@ -133,7 +117,7 @@ open internal class DecomposedCharSet(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override val name: String
|
override val name: String
|
||||||
get() = "decomposed char: $decomposedChar"
|
get() = "decomposed char: $decomposedChar"
|
||||||
|
|
||||||
/** Reads Unicode codepoint from [testString] starting from [strIndex] until [rightBound]. */
|
/** Reads Unicode codepoint from [testString] starting from [strIndex] until [rightBound]. */
|
||||||
fun codePointAt(strIndex: Int, testString: CharSequence, rightBound: Int): Int {
|
fun codePointAt(strIndex: Int, testString: CharSequence, rightBound: Int): Int {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ open internal class LeafQuantifierSet(var quantifier: Quantifier,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process occurrences between min and max.
|
// Process occurrences between min and max.
|
||||||
while ((max < 0 || occurrences < max) && index + leaf.charCount <= testString.length) {
|
while ((max == Quantifier.INF || occurrences < max) && index + leaf.charCount <= testString.length) {
|
||||||
val shift = leaf.accepts(index, testString)
|
val shift = leaf.accepts(index, testString)
|
||||||
if (shift < 1) {
|
if (shift < 1) {
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ internal class PossessiveLeafQuantifierSet(
|
|||||||
occurrences++
|
occurrences++
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((max >= 0 || occurrences < max) && index + leaf.charCount <= testString.length) {
|
while ((max == Quantifier.INF || occurrences < max) && index + leaf.charCount <= testString.length) {
|
||||||
val shift = leaf.accepts(index, testString)
|
val shift = leaf.accepts(index, testString)
|
||||||
if (shift < 1) {
|
if (shift < 1) {
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ internal class ReluctantLeafQuantifierSet(
|
|||||||
occurrences++
|
occurrences++
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (shift >= 1 && occurrences <= max)
|
} while (shift >= 1 && (max == Quantifier.INF || occurrences <= max))
|
||||||
|
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user