ICU-7273 add loading of custom data, with caching, test data and test code

X-SVN-Rev: 27578
This commit is contained in:
Markus Scherer 2010-02-16 23:43:22 +00:00
parent 9f6f2d83d7
commit 81234fecdb
13 changed files with 222 additions and 45 deletions

View File

@ -139,7 +139,6 @@ EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "makedata", "..\data\makedata.vcproj", "{D9DF7F2F-93B7-4810-B5CD-96F4F33C079B}"
ProjectSection(ProjectDependencies) = postProject
{C2BE5000-7501-4E87-9724-B8D82494FAE6} = {C2BE5000-7501-4E87-9724-B8D82494FAE6}
{F5213103-6CBE-46E6-B4CC-2570B6837D86} = {F5213103-6CBE-46E6-B4CC-2570B6837D86}
{F5281B04-A9E0-4680-BBA8-1D7F7D115458} = {F5281B04-A9E0-4680-BBA8-1D7F7D115458}
{97521D06-EC47-45D4-8BD0-9E16B3F93B2A} = {97521D06-EC47-45D4-8BD0-9E16B3F93B2A}
{C2B04507-2521-4801-BF0D-5FD79D6D518C} = {C2B04507-2521-4801-BF0D-5FD79D6D518C}
@ -156,6 +155,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "makedata", "..\data\makedat
{73C0A65B-D1F2-4DE1-B3A6-15DAD2C23F3D} = {73C0A65B-D1F2-4DE1-B3A6-15DAD2C23F3D}
{62D4B15D-7A90-4ECB-BA19-5E021D6A21BC} = {62D4B15D-7A90-4ECB-BA19-5E021D6A21BC}
{73632960-B3A6-464D-83A3-4B43365F19B8} = {73632960-B3A6-464D-83A3-4B43365F19B8}
{C7891A65-80AB-4245-912E-5F1E17B0E6C4} = {C7891A65-80AB-4245-912E-5F1E17B0E6C4}
{77C78066-746F-4EA6-B3FE-B8C8A4A97891} = {77C78066-746F-4EA6-B3FE-B8C8A4A97891}
{37FC2C7F-1904-4811-8955-2F478830EAD1} = {37FC2C7F-1904-4811-8955-2F478830EAD1}
{E4993E82-D68A-46CA-BAE0-9D35E172E46F} = {E4993E82-D68A-46CA-BAE0-9D35E172E46F}
@ -280,10 +280,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testplug", "..\tools\icuinf
EndProjectSection
EndProject
Global
GlobalSection(SubversionScc) = preSolution
Svn-Managed = True
Manager = AnkhSVN - Subversion Support for Visual Studio
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
@ -607,4 +603,8 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(SubversionScc) = preSolution
Svn-Managed = True
Manager = AnkhSVN - Subversion Support for Visual Studio
EndGlobalSection
EndGlobal

View File

@ -1,6 +1,6 @@
/*
******************************************************************************
* Copyright (C) 1997-2006, International Business Machines
* Copyright (C) 1997-2010, International Business Machines
* Corporation and others. All Rights Reserved.
******************************************************************************
* Date Name Description
@ -40,8 +40,8 @@ public:
/**
* Construct a hashtable
* @param keyComp Compartor for comparing the keys
* @param valueComp Compartor for comparing the values
* @param keyComp Comparator for comparing the keys
* @param valueComp Comparator for comparing the values
* @param status Error code
*/
Hashtable(UKeyComparator *keyComp, UValueComparator *valueComp, UErrorCode& status);
@ -86,9 +86,9 @@ public:
const UHashElement* nextElement(int32_t& pos) const;
UKeyComparator* setKeyCompartor(UKeyComparator*keyComp);
UKeyComparator* setKeyComparator(UKeyComparator*keyComp);
UValueComparator* setValueCompartor(UValueComparator* valueComp);
UValueComparator* setValueComparator(UValueComparator* valueComp);
UBool equals(const Hashtable& that) const;
private:
@ -190,11 +190,11 @@ inline void Hashtable::removeAll(void) {
uhash_removeAll(hash);
}
inline UKeyComparator* Hashtable::setKeyCompartor(UKeyComparator*keyComp){
inline UKeyComparator* Hashtable::setKeyComparator(UKeyComparator*keyComp){
return uhash_setKeyComparator(hash, keyComp);
}
inline UValueComparator* Hashtable::setValueCompartor(UValueComparator* valueComp){
inline UValueComparator* Hashtable::setValueComparator(UValueComparator* valueComp){
return uhash_setValueComparator(hash, valueComp);
}

View File

@ -27,6 +27,7 @@
#include "mutex.h"
#include "normalizer2impl.h"
#include "ucln_cmn.h"
#include "uhash.h"
U_NAMESPACE_BEGIN
@ -409,13 +410,21 @@ private:
STATIC_SIMPLE_SINGLETON(noopSingleton);
static UHashtable *cache=NULL;
U_CDECL_BEGIN
static void U_CALLCONV deleteNorm2AllModes(void *allModes) {
delete (Norm2AllModes *)allModes;
}
static UBool U_CALLCONV uprv_normalizer2_cleanup() {
Norm2AllModesSingleton(nfcSingleton, NULL).deleteInstance();
Norm2AllModesSingleton(nfkcSingleton, NULL).deleteInstance();
Norm2AllModesSingleton(nfkc_cfSingleton, NULL).deleteInstance();
Norm2Singleton(noopSingleton).deleteInstance();
uhash_close(cache);
cache=NULL;
return TRUE;
}
@ -534,8 +543,11 @@ Normalizer2::getInstance(const char *packageName,
if(U_FAILURE(errorCode)) {
return NULL;
}
if(name==NULL || *name==0) {
errorCode=U_ILLEGAL_ARGUMENT_ERROR;
}
Norm2AllModes *allModes=NULL;
if(packageName==NULL) {
Norm2AllModes *allModes=NULL;
if(0==uprv_strcmp(name, "nfc")) {
allModes=Norm2AllModesSingleton(nfcSingleton, "nfc").getInstance(errorCode);
} else if(0==uprv_strcmp(name, "nfkc")) {
@ -543,25 +555,64 @@ Normalizer2::getInstance(const char *packageName,
} else if(0==uprv_strcmp(name, "nfkc_cf")) {
allModes=Norm2AllModesSingleton(nfkc_cfSingleton, "nfkc_cf").getInstance(errorCode);
}
if(allModes!=NULL) {
switch(mode) {
case UNORM2_COMPOSE:
return &allModes->comp;
case UNORM2_DECOMPOSE:
return &allModes->decomp;
case UNORM2_FCD:
allModes->impl.getFCDTrie(errorCode);
return &allModes->fcd;
case UNORM2_COMPOSE_CONTIGUOUS:
return &allModes->fcc;
default:
break; // do nothing
}
if(allModes==NULL && U_SUCCESS(errorCode)) {
UHashtable *localCache;
{
Mutex lock;
localCache=cache;
if(localCache!=NULL) {
allModes=(Norm2AllModes *)uhash_get(localCache, name);
}
}
if(allModes==NULL) {
if(localCache==NULL) {
Mutex lock;
if(cache==NULL) {
cache=uhash_open(uhash_hashChars, uhash_compareChars, NULL, &errorCode);
if(U_FAILURE(errorCode)) {
return NULL;
}
uhash_setKeyDeleter(cache, uprv_free);
uhash_setValueDeleter(cache, deleteNorm2AllModes);
}
localCache=cache;
}
allModes=Norm2AllModes::createInstance(packageName, name, errorCode);
if(U_SUCCESS(errorCode)) {
Mutex lock;
void *temp=uhash_get(localCache, name);
if(temp==NULL) {
int32_t keyLength=uprv_strlen(name)+1;
char *nameCopy=(char *)uprv_malloc(keyLength);
if(nameCopy==NULL) {
errorCode=U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
uprv_memcpy(nameCopy, name, keyLength);
uhash_put(localCache, nameCopy, allModes, &errorCode);
} else {
// race condition
delete allModes;
allModes=(Norm2AllModes *)temp;
}
}
}
}
if(U_SUCCESS(errorCode)) {
// TODO: Real loading and caching...
errorCode=U_UNSUPPORTED_ERROR;
if(allModes!=NULL && U_SUCCESS(errorCode)) {
switch(mode) {
case UNORM2_COMPOSE:
return &allModes->comp;
case UNORM2_DECOMPOSE:
return &allModes->decomp;
case UNORM2_FCD:
allModes->impl.getFCDTrie(errorCode);
return &allModes->fcd;
case UNORM2_COMPOSE_CONTIGUOUS:
return &allModes->fcc;
default:
break; // do nothing
}
}
return NULL;
}

View File

@ -1,7 +1,7 @@
/*
*******************************************************************************
* Copyright (C) 2009, International Business Machines Corporation and *
* others. All Rights Reserved. *
* Copyright (C) 2009-2010, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*/
@ -347,7 +347,7 @@ CurrencyPluralInfo::initHash(UErrorCode& status) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
hTable->setValueCompartor(ValueComparator);
hTable->setValueComparator(ValueComparator);
return hTable;
}

View File

@ -4604,7 +4604,7 @@ DecimalFormat::initHashForAffix(UErrorCode& status) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
hTable->setValueCompartor(decimfmtAffixValueComparator);
hTable->setValueComparator(decimfmtAffixValueComparator);
return hTable;
}
@ -4618,7 +4618,7 @@ DecimalFormat::initHashForAffixPattern(UErrorCode& status) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
hTable->setValueCompartor(decimfmtAffixPatternValueComparator);
hTable->setValueComparator(decimfmtAffixPatternValueComparator);
return hTable;
}

View File

@ -602,7 +602,7 @@ DateIntervalInfo::initHash(UErrorCode& status) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
hTable->setValueCompartor(dtitvinfHashTableValueComparator);
hTable->setValueComparator(dtitvinfHashTableValueComparator);
return hTable;
}

View File

@ -822,7 +822,7 @@ TimeUnitFormat::initHash(UErrorCode& status) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
hTable->setValueCompartor(tmutfmtHashTableValueComparator);
hTable->setValueComparator(tmutfmtHashTableValueComparator);
return hTable;
}

View File

@ -9,6 +9,7 @@
#if !UCONFIG_NO_NORMALIZATION
#include "unicode/uchar.h"
#include "unicode/errorcode.h"
#include "unicode/normlzr.h"
#include "unicode/uniset.h"
#include "unicode/usetiter.h"
@ -51,6 +52,7 @@ void BasicNormalizerTest::runIndexedTest(int32_t index, UBool exec,
CASE(14,FindFoldFCDExceptions);
CASE(15,TestCompare);
CASE(16,TestSkippable);
CASE(17,TestCustomComp);
default: name = ""; break;
}
}
@ -1754,4 +1756,37 @@ BasicNormalizerTest::TestSkippable() {
}
}
struct StringPair { const char *input, *expected; };
void
BasicNormalizerTest::TestCustomComp() {
static const StringPair pairs[]={
{ "\\uD801\\uE000\\uDFFE", "" },
{ "\\uD800\\uD801\\uE000\\uDFFE\\uDFFF", "\\uD7FF\\uFFFF" },
{ "\\uD800\\uD801\\uDFFE\\uDFFF", "\\uD7FF\\U000107FE\\uFFFF" },
{ "\\uE001\\U000110B9\\u0345\\u0308\\u0327", "\\uE002\\U000110B9\\u0327\\u0345" },
{ "\\uE010\\U000F0011\\uE012", "\\uE011\\uE012" },
{ "\\uE010\\U000F0011\\U000F0011\\uE012", "\\uE011\\U000F0010" },
{ "\\uE111\\u1161\\uE112\\u1162", "\\uAE4C\\u1102\\u0062\\u1162" },
{ "\\uFFF3\\uFFF7\\U00010036\\U00010077", "\\U00010037\\U00010037\\uFFF6\\U00010037" }
};
IcuTestErrorCode errorCode(*this, "BasicNormalizerTest/TestCustomComp");
const Normalizer2 *customComp=
Normalizer2::getInstance(loadTestData(errorCode), "testnorm", UNORM2_COMPOSE, errorCode);
if(errorCode.isFailure()) {
errorCode.reset();
dataerrln(UNICODE_STRING_SIMPLE("unable to load testdata/testnorm.nrm"));
return;
}
for(int32_t i=0; i<LENGTHOF(pairs); ++i) {
const StringPair &pair=pairs[i];
UnicodeString input=UnicodeString(pair.input, -1, US_INV).unescape();
UnicodeString expected=UnicodeString(pair.expected, -1, US_INV).unescape();
UnicodeString result=customComp->normalize(input, errorCode);
if(result!=expected) {
errln("custom compose Normalizer2 did not normalize input %d as expected", i);
}
}
}
#endif /* #if !UCONFIG_NO_NORMALIZATION */

View File

@ -1,6 +1,6 @@
/********************************************************************
* COPYRIGHT:
* Copyright (c) 1997-2003, International Business Machines Corporation and
* Copyright (c) 1997-2010, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************/
@ -42,6 +42,7 @@ public:
void TestCompare(void);
void FindFoldFCDExceptions();
void TestSkippable();
void TestCustomComp();
private:
UnicodeString canonTests[24][3];

View File

@ -1,6 +1,6 @@
/********************************************************************
* COPYRIGHT:
* Copyright (c) 2004-2009, International Business Machines Corporation and
* Copyright (c) 2004-2010, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************/
@ -178,21 +178,21 @@ void UVectorTest::Hashtable_API() {
TEST_ASSERT((a->removei("a") == 1));
TEST_ASSERT((a->find("a") == NULL));
/* verify that setValueCompartor works */
/* verify that setValueComparator works */
Hashtable b(status);
TEST_ASSERT((!a->equals(b)));
TEST_ASSERT((b.puti("b", 2, status) == 0));
TEST_ASSERT((!a->equals(b))); // Without a value comparator, this will be FALSE by default.
b.setValueCompartor(uhash_compareLong);
b.setValueComparator(uhash_compareLong);
TEST_ASSERT((!a->equals(b)));
a->setValueCompartor(uhash_compareLong);
a->setValueComparator(uhash_compareLong);
TEST_ASSERT((a->equals(b)));
TEST_ASSERT((a->equals(*a))); // This better be reflexive.
/* verify that setKeyCompartor works */
/* verify that setKeyComparator works */
TEST_ASSERT((a->puti("a", 1, status) == 0));
TEST_ASSERT((a->find("a") != NULL));
a->setKeyCompartor(neverTRUE);
a->setKeyComparator(neverTRUE);
TEST_ASSERT((a->find("a") == NULL));
delete a;

View File

@ -123,6 +123,8 @@ TEST_UCM_SOURCE= test1.ucm test1bmp.ucm test3.ucm test4.ucm test4x.ucm test5.ucm
TEST_UCM_FILES=$(TEST_UCM_SOURCE:%=$(TESTSRCDATADIR)/data/%)
TEST_CNV_FILES=$(TEST_UCM_SOURCE:%.ucm=$(TESTBUILDDIR)/%.cnv)
TEST_NRM_FILES=$(TESTBUILDDIR)/testnorm.nrm
# import the shared .mk file
include $(TESTSRCDATADIR)/tstfiles.mk
-include $(TESTSRCDATADIR)/tstlocal.mk
@ -138,7 +140,7 @@ ALL_TEST_FILES = $(TEST_DAT_FILES) $(TEST_SPP_FILES) $(TEST_BRK_FILES) $(TEST_CN
$(TESTBUILDDIR)/testdata.lst: $(SRCLISTDEPS)
@echo "generating $@ (list of data files)"
@-$(RMV) $@
@for file in $(TEST_RES_FILES:$(TESTBUILDDIR)/%.res=%.res) $(TEST_DAT_FILES:$(TESTBUILDDIR)/%.icu=%.icu) $(TEST_SPP_FILES:$(TESTBUILDDIR)/%.spp=%.spp) $(TEST_CNV_FILES:$(TESTBUILDDIR)/%.cnv=%.cnv); do \
@for file in $(TEST_RES_FILES:$(TESTBUILDDIR)/%.res=%.res) $(TEST_DAT_FILES:$(TESTBUILDDIR)/%.icu=%.icu) $(TEST_SPP_FILES:$(TESTBUILDDIR)/%.spp=%.spp) $(TEST_CNV_FILES:$(TESTBUILDDIR)/%.cnv=%.cnv) $(TEST_NRM_FILES:$(TESTBUILDDIR)/%.nrm=%.nrm); do \
echo $$file >> $@; \
done;
@ -183,6 +185,9 @@ $(TESTBUILDDIR)/nfsmxp.spp: $(TOOLBINDIR)/gensprep$(EXEEXT) $(TESTSRCDATADIR)/nf
$(TESTBUILDDIR)/%.cnv: $(TESTSRCDATADIR)/%.ucm $(TOOLBINDIR)/makeconv$(EXEEXT)
$(INVOKE) $(TOOLBINDIR)/makeconv --small -c -d $(TESTBUILDDIR) $(TESTSRCDATADIR)/$(<F)
$(TESTBUILDDIR)/%.nrm: $(TESTSRCDATADIR)/%.txt $(TOOLBINDIR)/gennorm2$(EXEEXT)
$(INVOKE) $(TOOLBINDIR)/gennorm2 -s $(TESTSRCDATADIR) $(<F) -o $@
$(TESTBUILDDIR)/%.res: $(TESTSRCDATADIR)/%.txt $(TOOLBINDIR)/genrb$(EXEEXT) $(DAT_FILES)
$(INVOKE) $(TOOLBINDIR)/genrb $(GENRBOPTS) -q -s $(TESTSRCDATADIR) $(ICU_DATA_OPT) -d $(TESTBUILDDIR) $(<F)

View File

@ -28,7 +28,7 @@ ALL : "$(TESTDATAOUT)\testdata.dat"
TEST_RES_FILES = $(TEST_RES_SOURCE:.txt=.res)
"$(TESTDATAOUT)\testdata.dat" : $(TEST_RES_FILES) "$(TESTDATABLD)\casing.res" "$(TESTDATABLD)\conversion.res" "$(TESTDATABLD)\icuio.res" "$(TESTDATABLD)\mc.res" "$(TESTDATABLD)\structLocale.res" "$(TESTDATABLD)\root.res" "$(TESTDATABLD)\sh.res" "$(TESTDATABLD)\sh_YU.res" "$(TESTDATABLD)\te.res" "$(TESTDATABLD)\te_IN.res" "$(TESTDATABLD)\te_IN_REVISED.res" "$(TESTDATABLD)\testaliases.res" "$(TESTDATABLD)\testtypes.res" "$(TESTDATABLD)\testempty.res" "$(TESTDATABLD)\iscii.res" "$(TESTDATABLD)\idna_rules.res" "$(TESTDATABLD)\DataDrivenCollationTest.res" "$(TESTDATABLD)\test.icu" "$(TESTDATABLD)\testtable32.res" "$(TESTDATABLD)\test1.cnv" "$(TESTDATABLD)\test1bmp.cnv" "$(TESTDATABLD)\test3.cnv" "$(TESTDATABLD)\test4.cnv" "$(TESTDATABLD)\test4x.cnv" "$(TESTDATABLD)\test5.cnv" "$(TESTDATABLD)\ibm9027.cnv" "$(TESTDATABLD)\nfscsi.spp" "$(TESTDATABLD)\nfscss.spp" "$(TESTDATABLD)\nfscis.spp" "$(TESTDATABLD)\nfsmxs.spp" "$(TESTDATABLD)\nfsmxp.spp"
"$(TESTDATAOUT)\testdata.dat" : $(TEST_RES_FILES) "$(TESTDATABLD)\casing.res" "$(TESTDATABLD)\conversion.res" "$(TESTDATABLD)\icuio.res" "$(TESTDATABLD)\mc.res" "$(TESTDATABLD)\structLocale.res" "$(TESTDATABLD)\root.res" "$(TESTDATABLD)\sh.res" "$(TESTDATABLD)\sh_YU.res" "$(TESTDATABLD)\te.res" "$(TESTDATABLD)\te_IN.res" "$(TESTDATABLD)\te_IN_REVISED.res" "$(TESTDATABLD)\testaliases.res" "$(TESTDATABLD)\testtypes.res" "$(TESTDATABLD)\testempty.res" "$(TESTDATABLD)\iscii.res" "$(TESTDATABLD)\idna_rules.res" "$(TESTDATABLD)\DataDrivenCollationTest.res" "$(TESTDATABLD)\test.icu" "$(TESTDATABLD)\testtable32.res" "$(TESTDATABLD)\test1.cnv" "$(TESTDATABLD)\test1bmp.cnv" "$(TESTDATABLD)\test3.cnv" "$(TESTDATABLD)\test4.cnv" "$(TESTDATABLD)\test4x.cnv" "$(TESTDATABLD)\test5.cnv" "$(TESTDATABLD)\ibm9027.cnv" "$(TESTDATABLD)\nfscsi.spp" "$(TESTDATABLD)\nfscss.spp" "$(TESTDATABLD)\nfscis.spp" "$(TESTDATABLD)\nfsmxs.spp" "$(TESTDATABLD)\nfsmxp.spp" "$(TESTDATABLD)\testnorm.nrm"
@echo Building test data
@copy "$(TESTDATABLD)\te.res" "$(TESTDATAOUT)\$(TESTDT)\nam.typ"
@copy "$(TESTDATA)\old_l_testtypes.res" "$(TESTDATABLD)"
@ -66,6 +66,7 @@ nfscss.spp
nfscis.spp
nfsmxs.spp
nfsmxp.spp
testnorm.nrm
$(TEST_RES_FILES:.res =.res
)
<<
@ -151,3 +152,8 @@ $(TEST_RES_FILES:.res =.res
"$(TESTDATABLD)\ibm9027.cnv": "$(TESTDATA)\ibm9027.ucm"
@echo Building $@
@"$(ICUTOOLS)\makeconv\$(CFG)\makeconv" --small -d"$(TESTDATABLD)" $**
# Target for test normalization data
"$(TESTDATABLD)\testnorm.nrm": "$(TESTDATA)\testnorm.txt"
@echo Building $@
@"$(ICUTOOLS)\gennorm2\$(CFG)\gennorm2" -s "$(TESTDATA)" testnorm.txt -o $@

79
icu4c/source/test/testdata/testnorm.txt vendored Normal file
View File

@ -0,0 +1,79 @@
# Copyright (C) 2010, International Business Machines
# Corporation and others. All Rights Reserved.
#
# file name: testnorm.txt
# encoding: US-ASCII
# tab size: 8 (not used)
# indentation:4
#
# created on: 2010feb15
# created by: Markus W. Scherer
#
# Normalization test data, for improving code coverage.
# Selection of Canonical_Combining_Class (ccc) values
0300..0314:230
0315:232
0316..0319:220
031A:232
031B:216
031C..0320:220
0321..0322:202
0323..0326:220
0327..0328:202
0329..0333:220
0334..0338:1
0339..033C:220
033D..0344:230
0345:240
0346:230
0347..0349:220
034A..034C:230
034D..034E:220
0350..0352:230
0353..0356:220
0357:230
0358:232
0359..035A:220
035B:230
035C:233
035D..035E:234
035F:233
0360..0361:234
0362:233
0363..036F:230
D802:2 # surrogates with non-zero combining classes
D803:3
D804:4
110B9:9
110BA:7
# Some interesting mappings
00C0=0041 0300
00C1=0041 0301
00C2=0041 0302
00C3=0041 0303
00C4=0041 0308
00C5=0041 030A
00C7=0043 0327
D800>D7FF # surrogates with mappings, and mappings to empty strings
D801>
DFFE>
DFFF>FFFF
E000>
E001=61 338 # composition with trail<=33FF and composite>7FFF
E002=E001 308 # recursive mapping needs reordering
E003>62 307 327 337 # mapping needs reordering
E011=E010 F0011 # composition of BMP+supplementary, and F0011 is maybe & combines-fwd
E111>1101 # mapping ends in Jamo L
E112>1102 62 # mapping starts with Jamo L
FFF3>FFF4
FFF4>FFF5
FFF5>FFF7
FFF7>10037
10036>FFF6
10077>10037
1109A=11099 110BA
1109C=1109B 110BA
110AB=110A5 110BA
F0010=F0011 E012 # composition of supplementary+BMP