diff --git a/UnicodeInformation.Builder/UnicodeCharacterDataBuilder.cs b/UnicodeInformation.Builder/UnicodeCharacterDataBuilder.cs index aed085c..931b67d 100644 --- a/UnicodeInformation.Builder/UnicodeCharacterDataBuilder.cs +++ b/UnicodeInformation.Builder/UnicodeCharacterDataBuilder.cs @@ -1,129 +1,44 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace System.Unicode.Builder { public sealed class UnicodeCharacterDataBuilder { - private readonly UnicodeCodePointRange codePointRange; - private string name; private UnicodeCategory category = UnicodeCategory.OtherNotAssigned; - private CanonicalCombiningClass canonicalCombiningClass; - private BidirectionalClass bidirectionalClass; - private CharacterDecompositionMapping characterDecompositionMapping; - private UnicodeNumericType numericType; - private UnicodeRationalNumber numericValue; - private bool bidirectionalMirrored; - private string oldName; - private string simpleUpperCaseMapping; - private string simpleLowerCaseMapping; - private string simpleTitleCaseMapping; - private ContributoryProperties contributoryProperties; - private CoreProperties coreProperties; private readonly List nameAliases = new List(); private readonly List crossRerefences = new List(); - public UnicodeCodePointRange CodePointRange { get { return codePointRange; } } + public UnicodeCodePointRange CodePointRange { get; } - public string Name - { - get { return name; } - set { name = value; } - } + public string Name { get; set; } public IList NameAliases { get { return nameAliases; } } public UnicodeCategory Category { - get { return category; } - set - { - if (!Enum.IsDefined(typeof(UnicodeCategory), value)) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - category = value; - } - } - - public CanonicalCombiningClass CanonicalCombiningClass - { - get { return canonicalCombiningClass; } - set { canonicalCombiningClass = value; } // Even values not defined in the enum are allowed here. - } - - public BidirectionalClass BidirectionalClass - { - get { return bidirectionalClass; } - set { bidirectionalClass = value; } - } - - public CharacterDecompositionMapping CharacterDecompositionMapping - { - get { return characterDecompositionMapping; } - set { characterDecompositionMapping = value; } - } - - public UnicodeNumericType NumericType - { - get { return numericType; } - set { numericType = value; } - } - - public UnicodeRationalNumber NumericValue - { - get { return numericValue; } - set { numericValue = value; } - } - - public string OldName - { - get { return oldName; } - set { oldName = value; } - } - - public bool BidirectionalMirrored - { - get { return bidirectionalMirrored; } - set { bidirectionalMirrored = value; } - } - - public string SimpleUpperCaseMapping - { - get { return simpleUpperCaseMapping; } - set { simpleUpperCaseMapping = value; } - } - - public string SimpleLowerCaseMapping - { - get { return simpleLowerCaseMapping; } - set { simpleLowerCaseMapping = value; } - } - - public string SimpleTitleCaseMapping - { - get { return simpleTitleCaseMapping; } - set { simpleTitleCaseMapping = value; } - } - - public ContributoryProperties ContributoryProperties - { - get { return contributoryProperties; } - set { contributoryProperties = value; } - } - - public CoreProperties CoreProperties - { - get { return coreProperties; } - set { coreProperties = value; } + get => category; + set => category = Enum.IsDefined(typeof(UnicodeCategory), value) ? + value : + throw new ArgumentOutOfRangeException(nameof(value)); } + public CanonicalCombiningClass CanonicalCombiningClass { get; set; } // Even values not defined in the enum are allowed here. + public BidirectionalClass BidirectionalClass { get; set; } + public CharacterDecompositionMapping CharacterDecompositionMapping { get; set; } + public UnicodeNumericType NumericType { get; set; } + public UnicodeRationalNumber NumericValue { get; set; } + public string OldName { get; set; } + public bool BidirectionalMirrored { get; set; } + public string SimpleUpperCaseMapping { get; set; } + public string SimpleLowerCaseMapping { get; set; } + public string SimpleTitleCaseMapping { get; set; } + public ContributoryProperties ContributoryProperties { get; set; } + public CoreProperties CoreProperties { get; set; } + public EmojiProperties EmojiProperties { get; set; } public IList CrossRerefences { get { return crossRerefences; } } public UnicodeCharacterDataBuilder(int codePoint) @@ -133,7 +48,7 @@ namespace System.Unicode.Builder public UnicodeCharacterDataBuilder(UnicodeCodePointRange codePointRange) { - this.codePointRange = codePointRange; + CodePointRange = codePointRange; this.category = UnicodeCategory.OtherNotAssigned; } @@ -143,7 +58,7 @@ namespace System.Unicode.Builder ( CodePointRange, Name, - NameAliases.Count > 0 ? NameAliases.ToArray() : UnicodeNameAlias.EmptyArray, + nameAliases.Count > 0 ? nameAliases.ToArray() : UnicodeNameAlias.EmptyArray, Category, CanonicalCombiningClass, BidirectionalClass, @@ -157,7 +72,7 @@ namespace System.Unicode.Builder SimpleLowerCaseMapping, SimpleTitleCaseMapping, ContributoryProperties, - CoreProperties, + (int)CoreProperties | (int)EmojiProperties << 20, CrossRerefences.Count > 0 ? CrossRerefences.ToArray() : null ); } @@ -168,27 +83,27 @@ namespace System.Unicode.Builder UcdFields fields = default(UcdFields); - if (!codePointRange.IsSingleCodePoint) fields = UcdFields.CodePointRange; + if (!CodePointRange.IsSingleCodePoint) fields = UcdFields.CodePointRange; - if (name != null || nameAliases.Count > 0) fields |= UcdFields.Name; // This field combines name and alias. + if (Name != null || nameAliases.Count > 0) fields |= UcdFields.Name; // This field combines name and alias. if (category != UnicodeCategory.OtherNotAssigned) fields |= UcdFields.Category; - if (canonicalCombiningClass != CanonicalCombiningClass.NotReordered) fields |= UcdFields.CanonicalCombiningClass; + if (CanonicalCombiningClass != CanonicalCombiningClass.NotReordered) fields |= UcdFields.CanonicalCombiningClass; /*if (bidirectionalClass != 0)*/ fields |= UcdFields.BidirectionalClass; - if (characterDecompositionMapping.DecompositionMapping != null) fields |= UcdFields.DecompositionMapping; - fields |= (UcdFields)((int)numericType << 6); - if (bidirectionalMirrored) fields |= UcdFields.BidirectionalMirrored; - if (oldName != null) fields |= UcdFields.OldName; - if (simpleUpperCaseMapping != null) fields |= UcdFields.SimpleUpperCaseMapping; - if (simpleLowerCaseMapping != null) fields |= UcdFields.SimpleLowerCaseMapping; - if (simpleTitleCaseMapping != null) fields |= UcdFields.SimpleTitleCaseMapping; - if (contributoryProperties != 0) fields |= UcdFields.ContributoryProperties; - if (coreProperties != 0) fields |= UcdFields.CoreProperties; + if (CharacterDecompositionMapping.DecompositionMapping != null) fields |= UcdFields.DecompositionMapping; + fields |= (UcdFields)((int)NumericType << 6); + if (BidirectionalMirrored) fields |= UcdFields.BidirectionalMirrored; + if (OldName != null) fields |= UcdFields.OldName; + if (SimpleUpperCaseMapping != null) fields |= UcdFields.SimpleUpperCaseMapping; + if (SimpleLowerCaseMapping != null) fields |= UcdFields.SimpleLowerCaseMapping; + if (SimpleTitleCaseMapping != null) fields |= UcdFields.SimpleTitleCaseMapping; + if (ContributoryProperties != 0) fields |= UcdFields.ContributoryProperties; + if (CoreProperties != 0 || EmojiProperties != 0) fields |= UcdFields.CorePropertiesAndEmojiProperties; if (crossRerefences.Count > 0) fields |= UcdFields.CrossRerefences; writer.Write((ushort)fields); - writer.WriteCodePoint(codePointRange.FirstCodePoint); + writer.WriteCodePoint(CodePointRange.FirstCodePoint); if ((fields & UcdFields.CodePointRange) != 0) writer.WriteCodePoint(CodePointRange.LastCodePoint); if ((fields & UcdFields.Name) != 0) @@ -200,38 +115,38 @@ namespace System.Unicode.Builder if (nameAliases.Count > 0) { - writer.WritePackedLength((byte)(name != null ? 3 : 2), nameAliases.Count); + writer.WritePackedLength((byte)(Name != null ? 3 : 2), nameAliases.Count); - if (name != null) - writer.WriteNamePropertyToFile(name); + if (Name != null) + writer.WriteNamePropertyToFile(Name); foreach (var nameAlias in nameAliases) writer.WriteNameAliasToFile(nameAlias); } else { - writer.WriteNamePropertyToFile(name); + writer.WriteNamePropertyToFile(Name); } } if ((fields & UcdFields.Category) != 0) writer.Write((byte)category); - if ((fields & UcdFields.CanonicalCombiningClass) != 0) writer.Write((byte)canonicalCombiningClass); - if ((fields & UcdFields.BidirectionalClass) != 0) writer.Write((byte)bidirectionalClass); + if ((fields & UcdFields.CanonicalCombiningClass) != 0) writer.Write((byte)CanonicalCombiningClass); + if ((fields & UcdFields.BidirectionalClass) != 0) writer.Write((byte)BidirectionalClass); if ((fields & UcdFields.DecompositionMapping) != 0) { - writer.Write((byte)characterDecompositionMapping.DecompositionType); - writer.Write(characterDecompositionMapping.DecompositionMapping); + writer.Write((byte)CharacterDecompositionMapping.DecompositionType); + writer.Write(CharacterDecompositionMapping.DecompositionMapping); } if ((fields & UcdFields.NumericNumeric) != 0) { - writer.Write(numericValue.Numerator); - writer.Write(numericValue.Denominator); + writer.Write(NumericValue.Numerator); + writer.Write(NumericValue.Denominator); } - if ((fields & UcdFields.OldName) != 0) writer.Write(oldName); - if ((fields & UcdFields.SimpleUpperCaseMapping) != 0) writer.Write(simpleUpperCaseMapping); - if ((fields & UcdFields.SimpleLowerCaseMapping) != 0) writer.Write(simpleLowerCaseMapping); - if ((fields & UcdFields.SimpleTitleCaseMapping) != 0) writer.Write(simpleTitleCaseMapping); - if ((fields & UcdFields.ContributoryProperties) != 0) writer.Write((int)contributoryProperties); - if ((fields & UcdFields.CoreProperties) != 0) writer.WriteUInt24((int)coreProperties); + if ((fields & UcdFields.OldName) != 0) writer.Write(OldName); + if ((fields & UcdFields.SimpleUpperCaseMapping) != 0) writer.Write(SimpleUpperCaseMapping); + if ((fields & UcdFields.SimpleLowerCaseMapping) != 0) writer.Write(SimpleLowerCaseMapping); + if ((fields & UcdFields.SimpleTitleCaseMapping) != 0) writer.Write(SimpleTitleCaseMapping); + if ((fields & UcdFields.ContributoryProperties) != 0) writer.Write((int)ContributoryProperties); + if ((fields & UcdFields.CorePropertiesAndEmojiProperties) != 0) writer.WriteUInt24((int)CoreProperties | (int)EmojiProperties << 20); if ((fields & UcdFields.CrossRerefences) != 0) { writer.Write(checked((byte)(crossRerefences.Count - 1))); diff --git a/UnicodeInformation.Tests/UnicodeInfoTests.cs b/UnicodeInformation.Tests/UnicodeInfoTests.cs index e1178df..76c7c59 100644 --- a/UnicodeInformation.Tests/UnicodeInfoTests.cs +++ b/UnicodeInformation.Tests/UnicodeInfoTests.cs @@ -51,6 +51,7 @@ namespace UnicodeInformation.Tests [InlineData(0x0D5D, UnicodeCategory.OtherNumber, UnicodeNumericType.Numeric, "3/20", "MALAYALAM FRACTION THREE TWENTIETHS", "Malayalam")] [InlineData(0x0D59, UnicodeCategory.OtherNumber, UnicodeNumericType.Numeric, "1/40", "MALAYALAM FRACTION ONE FORTIETH", "Malayalam")] [InlineData(0x1F953, UnicodeCategory.OtherSymbol, UnicodeNumericType.None, null, "BACON", "Supplemental Symbols and Pictographs")] + [InlineData(0x1F966, UnicodeCategory.OtherSymbol, UnicodeNumericType.None, null, "BROCCOLI", "Supplemental Symbols and Pictographs")] public void CharacterInfoShouldHaveExpectedResults(int codePoint, UnicodeCategory expectedCategory, UnicodeNumericType expectedNumericType, string expectedNumericValue, string expectedName, string expectedBlock) { var info = UnicodeInfo.GetCharInfo(codePoint); diff --git a/UnicodeInformation/CoreProperties.cs b/UnicodeInformation/CoreProperties.cs index 6f5aa78..e2c2e36 100644 --- a/UnicodeInformation/CoreProperties.cs +++ b/UnicodeInformation/CoreProperties.cs @@ -12,6 +12,8 @@ namespace System.Unicode [Flags] public enum CoreProperties : int { + // NB: Be careful when adding new properties to the enum, as EmojiProperties will be stored in the 4 upper bits out of 24. + /// Represents the Lowercase property. [ValueName("Lowercase"), ValueName("Lower"), Display(Name = "Lowercase")] Lowercase = 0x00000001, diff --git a/UnicodeInformation/EmojiProperties.cs b/UnicodeInformation/EmojiProperties.cs new file mode 100644 index 0000000..eac0e11 --- /dev/null +++ b/UnicodeInformation/EmojiProperties.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace System.Unicode +{ + /// A bitmask of the various available emoji properties. + /// Emoji properties are not formally part of UCD, but . + [Flags] + public enum EmojiProperties : byte + { + // NB: These values will be stored in as the most significant bits of CoreProperties. + // CoreProperties are stored using 18 bits out of 24 available bits, leaving at most 6 bits to use. 4 of those will be used by emoji properties. + + /// Represents the Emoji property. + [ValueName("Emoji"), Display(Name = "Emoji")] + Emoji = 0x1, + /// Represents the Emoji property. + [ValueName("Emoji_Presentation"), Display(Name = "Emoji_Presentation")] + EmojiPresentation = 0x2, + /// Represents the Emoji property. + [ValueName("Emoji_Modifier_Base"), Display(Name = "Emoji_Modifier_Base")] + EmojiModifierBase = 0x4, + /// Represents the Emoji property. + [ValueName("Emoji_Component"), Display(Name = "Emoji_Component")] + EmojiComponent = 0x8, + } +} diff --git a/UnicodeInformation/UcdFields.cs b/UnicodeInformation/UcdFields.cs index 8714ba4..e0959d4 100644 --- a/UnicodeInformation/UcdFields.cs +++ b/UnicodeInformation/UcdFields.cs @@ -34,7 +34,7 @@ namespace System.Unicode SimpleTitleCaseMapping = 4096, ContributoryProperties = 8192, - CoreProperties = 16384, + CorePropertiesAndEmojiProperties = 16384, CrossRerefences = 32768, } diff --git a/UnicodeInformation/UnicodeCharInfo.cs b/UnicodeInformation/UnicodeCharInfo.cs index 00a4815..46f0e51 100644 --- a/UnicodeInformation/UnicodeCharInfo.cs +++ b/UnicodeInformation/UnicodeCharInfo.cs @@ -14,46 +14,49 @@ namespace System.Unicode private readonly int codePoint; /// The name of the code point. private readonly string name; - private readonly UnicodeCharacterData unicodeCharacterData; - private readonly UnihanCharacterData unihanCharacterData; + private readonly int unicodeCharacterDataIndex; + private readonly int unihanCharacterDataIndex; private readonly string block; + private ref UnicodeCharacterData UnicodeCharacterData => ref UnicodeInfo.GetUnicodeCharacterData(unicodeCharacterDataIndex); + private ref UnihanCharacterData UnihanCharacterData => ref UnicodeInfo.GetUnihanCharacterData(unihanCharacterDataIndex); + /// Gets the code point as an UTF-32 value. - public int CodePoint { get { return codePoint; } } + public int CodePoint => codePoint; /// Gets the code point name. /// This is the Name Unicode property. [ValueName("Name"), ValueName("na")] - public string Name { get { return name; } } + public string Name => name; /// Gets the name aliases defined for the code point. /// This is the Name_Alias Unicode property. [ValueName("Name_Alias")] - public UnicodeNameAliasCollection NameAliases { get { return new UnicodeNameAliasCollection(unicodeCharacterData?.NameAliases); } } + public UnicodeNameAliasCollection NameAliases => new UnicodeNameAliasCollection(UnicodeCharacterData.NameAliases); /// Gets the category defined for the code point. /// This is the General_Category Unicode property. [ValueName("General_Category"), ValueName("gc")] - public UnicodeCategory Category { get { return unicodeCharacterData?.Category ?? UnicodeCategory.OtherNotAssigned; } } + public UnicodeCategory Category => UnicodeCharacterData.Category; /// Gets the name of the block where the code point is located. /// This is the Block Unicode property. [ValueName("Block"), ValueName("blk")] - public string Block { get { return block ?? UnicodeInfo.DefaultBlock; } } + public string Block => block ?? UnicodeInfo.DefaultBlock; /// Gets the canonical combining class defined for the code point. /// This is the Canonical_Combining_Class Unicode property. [ValueName("Canonical_Combining_Class"), ValueName("ccc")] - public CanonicalCombiningClass CanonicalCombiningClass { get { return unicodeCharacterData?.CanonicalCombiningClass ?? CanonicalCombiningClass.NotReordered; } } + public CanonicalCombiningClass CanonicalCombiningClass => UnicodeCharacterData.CanonicalCombiningClass; /// Gets the bidirectional class defined for the code point. /// This is the Bidi_Class Unicode property. [ValueName("Bidi_Class"), ValueName("bc")] - public BidirectionalClass BidirectionalClass { get { return unicodeCharacterData?.BidirectionalClass ?? BidirectionalClass.LeftToRight; } } + public BidirectionalClass BidirectionalClass => UnicodeCharacterData.BidirectionalClass; /// Gets the decomposition type defined for the code point. /// This is the Decomposition_Type Unicode property. [ValueName("Decomposition_Type"), ValueName("dt")] - public CompatibilityFormattingTag DecompositionType { get { return unicodeCharacterData?.DecompositionType ?? CompatibilityFormattingTag.Canonical; } } + public CompatibilityFormattingTag DecompositionType => UnicodeCharacterData.DecompositionType; /// Gets the decomposition mapping defined for the code point. /// This is the Decomposition_Mapping Unicode property. [ValueName("Decomposition_Mapping"), ValueName("dm")] - public string DecompositionMapping { get { return unicodeCharacterData?.DecompositionMapping; } } + public string DecompositionMapping => UnicodeCharacterData.DecompositionMapping; /// Gets the numeric type defined for the code point. /// /// This is the Numeric_Type Unicode property. @@ -62,10 +65,10 @@ namespace System.Unicode /// In this case, the property will indicate the origin of the numeric value in Unihan data. /// [ValueName("Numeric_Type"), ValueName("nt")] - public UnicodeNumericType NumericType { get { return unihanCharacterData != null ? unihanCharacterData.NumericType != UnihanNumericType.None ? UnicodeNumericType.Numeric : UnicodeNumericType.None : unicodeCharacterData?.NumericType ?? UnicodeNumericType.None; } } + public UnicodeNumericType NumericType => unihanCharacterDataIndex >= 0 ? UnihanCharacterData.NumericType != UnihanNumericType.None ? UnicodeNumericType.Numeric : UnicodeNumericType.None : UnicodeCharacterData.NumericType; /// Gets the Unihan numeric type defined for the code point. /// The value of this property indicates which of the kPrimaryNumeric, kAccountingNumeric, or kOtherNumeric Unihan property is set, if any. - public UnihanNumericType UnihanNumericType { get { return unihanCharacterData != null ? unihanCharacterData.NumericType : UnihanNumericType.None; } } + public UnihanNumericType UnihanNumericType => unihanCharacterDataIndex >= 0 ? UnihanCharacterData.NumericType : UnihanNumericType.None; /// Gets the numeric value defined for the code point. /// /// This is the Numeric_Value Unicode property. @@ -73,47 +76,63 @@ namespace System.Unicode /// When this property set, the and indicates the nature of the numeric value. /// [ValueName("Numeric_Value"), ValueName("nv")] - public UnicodeRationalNumber? NumericValue { get { return unihanCharacterData != null && unihanCharacterData.NumericType != UnihanNumericType.None ? new UnicodeRationalNumber(unihanCharacterData.NumericValue, 1) : unicodeCharacterData?.NumericValue; } } + public UnicodeRationalNumber? NumericValue + { + get + { + if (unihanCharacterDataIndex >= 0) + { + ref var unihanCharacterData = ref UnihanCharacterData; + if (unihanCharacterData.NumericType != UnihanNumericType.None) + { + return new UnicodeRationalNumber(unihanCharacterData.NumericValue, 1); + } + } + + return UnicodeCharacterData.NumericValue; + } + } + /// Gets a value indicating whether the character is mirrored in bidirectional text. /// This is the Bidi_Mirrored Unicode property. [ValueName("Bidi_Mirrored")] - public bool BidirectionalMirrored { get { return unicodeCharacterData?.BidirectionalMirrored ?? false; } } + public bool BidirectionalMirrored => UnicodeCharacterData.BidirectionalMirrored; /// Gets the Unicode 1 name of the code point. /// This is the Unicode_1_Name Unicode property. [ValueName("Unicode_1_Name"), ValueName("na1")] - public string OldName { get { return unicodeCharacterData?.OldName; } } + public string OldName => UnicodeCharacterData.OldName; /// Gets the simple uppercase mapping defined for the code point. /// This is the Simple_Uppercase_Mapping Unicode property. [ValueName("Simple_Uppercase_Mapping"), ValueName("suc")] - public string SimpleUpperCaseMapping { get { return unicodeCharacterData?.SimpleUpperCaseMapping; } } + public string SimpleUpperCaseMapping => UnicodeCharacterData.SimpleUpperCaseMapping; /// Gets the simple lowercase mapping defined for the code point. /// This is the Simple_Lowercase_Mapping Unicode property. [ValueName("Simple_Lowercase_Mapping"), ValueName("slc")] - public string SimpleLowerCaseMapping { get { return unicodeCharacterData?.SimpleLowerCaseMapping; } } + public string SimpleLowerCaseMapping => UnicodeCharacterData.SimpleLowerCaseMapping; /// Gets the simple titlecase mapping defined for the code point. /// This is the Simple_Titlecase_Mapping Unicode property. [ValueName("Simple_Titlecase_Mapping"), ValueName("stc")] - public string SimpleTitleCaseMapping { get { return unicodeCharacterData?.SimpleTitleCaseMapping; } } + public string SimpleTitleCaseMapping => UnicodeCharacterData.SimpleTitleCaseMapping; /// Gets a value indicating which of the boolean contributory properties are defined for the code point. /// /// The Unicode standard indicates contributory properties as neither normative nor informational. /// However, contributory properties are used by Unicode to define the code properties. /// The corresponding core properties may be accessed from the member. /// - public ContributoryProperties ContributoryProperties { get { return unicodeCharacterData?.ContributoryProperties ?? 0; } } + public ContributoryProperties ContributoryProperties => UnicodeCharacterData.ContributoryProperties; /// Gets a value indicating which of the boolean core properties are defined for the code point. /// The core properties are computed by combining various character information together with contributory properties. - public CoreProperties CoreProperties { get { return unicodeCharacterData?.CoreProperties ?? 0; } } + public CoreProperties CoreProperties => UnicodeCharacterData.CoreProperties; /// Gets a collection of cross references associated with the code point. /// The cross references have been extracted from Unicode data but are not normative. - public UnicodeCrossReferenceCollection CrossRerefences { get { return new UnicodeCrossReferenceCollection(unicodeCharacterData?.CrossRerefences); } } + public UnicodeCrossReferenceCollection CrossRerefences => new UnicodeCrossReferenceCollection(UnicodeCharacterData.CrossRerefences); /// Gets the radical and stroke count for the code point. /// /// This is the Unicode_Radical_Stroke Unicode property, defined as kRSUnicode in Unihan data. /// This property is only ever useful when the character is a CJK ideograph. /// [ValueName("kRSUnicode"), ValueName("cjkRSUnicode"), ValueName("Unicode_Radical_Stroke"), ValueName("URS")] - public UnicodeRadicalStrokeCountCollection UnicodeRadicalStrokeCounts { get { return new UnicodeRadicalStrokeCountCollection(unihanCharacterData?.UnicodeRadicalStrokeCounts); } } + public UnicodeRadicalStrokeCountCollection UnicodeRadicalStrokeCounts => new UnicodeRadicalStrokeCountCollection(UnihanCharacterData.UnicodeRadicalStrokeCounts); /// Gets the definition of the character from the Unihan data. /// @@ -121,56 +140,56 @@ namespace System.Unicode /// This property is only ever useful when the character is a CJK ideograph. /// [ValueName("kDefinition")] - public string Definition { get { return unihanCharacterData?.Definition; } } + public string Definition => UnihanCharacterData.Definition; /// Gets the Mandarin reading of the character from the Unihan data. /// /// This is the kMandarin Unicode property. /// This property is only ever useful when the character is a CJK ideograph. /// [ValueName("kMandarin")] - public string MandarinReading { get { return unihanCharacterData?.MandarinReading; } } + public string MandarinReading => UnihanCharacterData.MandarinReading; /// Gets the Cantonese reading of the character from the Unihan data. /// /// This is the kCantonese Unicode property. /// This property is only ever useful when the character is a CJK ideograph. /// [ValueName("kCantonese")] - public string CantoneseReading { get { return unihanCharacterData?.CantoneseReading; } } + public string CantoneseReading => UnihanCharacterData.CantoneseReading; /// Gets the Japanese Kun reading of the character from the Unihan data. /// /// This is the kJapaneseKun Unicode property. /// This property is only ever useful when the character is a CJK ideograph. /// [ValueName("kJapaneseKun")] - public string JapaneseKunReading { get { return unihanCharacterData?.JapaneseKunReading; } } + public string JapaneseKunReading => UnihanCharacterData.JapaneseKunReading; /// Gets the Japanese On reading of the character from the Unihan data. /// /// This is the kJapaneseOn Unicode property. /// This property is only ever useful when the character is a CJK ideograph. /// [ValueName("kJapaneseOn")] - public string JapaneseOnReading { get { return unihanCharacterData?.JapaneseOnReading; } } + public string JapaneseOnReading => UnihanCharacterData.JapaneseOnReading; /// Gets the Korean reading of the character from the Unihan data. /// /// This is the kKorean Unicode property. /// This property is only ever useful when the character is a CJK ideograph. /// [ValueName("kKorean")] - public string KoreanReading { get { return unihanCharacterData?.KoreanReading; } } + public string KoreanReading => UnihanCharacterData.KoreanReading; /// Gets the Hangul reading of the character from the Unihan data. /// /// This is the kHangul Unicode property. /// This property is only ever useful when the character is a CJK ideograph. /// [ValueName("kHangul")] - public string HangulReading { get { return unihanCharacterData?.HangulReading; } } + public string HangulReading => UnihanCharacterData.HangulReading; /// Gets the Vietnamese reading of the character from the Unihan data. /// /// This is the kVietnamese Unicode property. /// This property is only ever useful when the character is a CJK ideograph. /// [ValueName("kVietnamese")] - public string VietnameseReading { get { return unihanCharacterData?.VietnameseReading; } } + public string VietnameseReading => UnihanCharacterData.VietnameseReading; /// Gets the simplified variant of the character from the Unihan data. /// @@ -178,21 +197,21 @@ namespace System.Unicode /// This property is only ever useful when the character is a CJK ideograph. /// [ValueName("kSimplifiedVariant")] - public string SimplifiedVariant { get { return unihanCharacterData?.SimplifiedVariant; } } + public string SimplifiedVariant => UnihanCharacterData.SimplifiedVariant; /// Gets the traditional variant of the character from the Unihan data. /// /// This is the kTraditionalVariant Unicode property. /// This property is only ever useful when the character is a CJK ideograph. /// [ValueName("kTraditionalVariant")] - public string TraditionalVariant { get { return unihanCharacterData?.TraditionalVariant; } } + public string TraditionalVariant => UnihanCharacterData.TraditionalVariant; - internal UnicodeCharInfo(int codePoint, UnicodeCharacterData unicodeCharacterData, UnihanCharacterData unihanCharacterData, string block) + internal UnicodeCharInfo(int codePoint, int unicodeCharacterDataIndex, int unihanCharacterDataIndex, string block) { this.codePoint = codePoint; - this.name = unicodeCharacterData != null ? UnicodeInfo.GetName(codePoint, unicodeCharacterData) : null; - this.unicodeCharacterData = unicodeCharacterData; - this.unihanCharacterData = unihanCharacterData; + this.name = unicodeCharacterDataIndex >= 0 ? UnicodeInfo.GetName(codePoint, ref UnicodeInfo.GetUnicodeCharacterData(unicodeCharacterDataIndex)) : null; + this.unicodeCharacterDataIndex = unicodeCharacterDataIndex; + this.unihanCharacterDataIndex = unihanCharacterDataIndex; this.block = block; } } diff --git a/UnicodeInformation/UnicodeCharacterData.cs b/UnicodeInformation/UnicodeCharacterData.cs index 82e0577..cccbecb 100644 --- a/UnicodeInformation/UnicodeCharacterData.cs +++ b/UnicodeInformation/UnicodeCharacterData.cs @@ -1,13 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Globalization; namespace System.Unicode { - internal sealed class UnicodeCharacterData + internal struct UnicodeCharacterData { public readonly UnicodeCodePointRange CodePointRange; public readonly string Name; @@ -25,7 +20,9 @@ namespace System.Unicode public readonly string SimpleLowerCaseMapping; public readonly string SimpleTitleCaseMapping; public readonly ContributoryProperties ContributoryProperties; - public readonly CoreProperties CoreProperties; + private readonly int corePropertiesAndEmojiProperties; + public CoreProperties CoreProperties => (CoreProperties)(corePropertiesAndEmojiProperties & 0x000FFFFF); + public EmojiProperties EmojiProperties => (EmojiProperties)(corePropertiesAndEmojiProperties >> 20); public readonly int[] CrossRerefences; // NB: It seems that parsing NamesList is required in order to provide data for this field ? @@ -47,7 +44,7 @@ namespace System.Unicode string simpleLowerCaseMapping, string simpleTitleCaseMapping, ContributoryProperties contributoryProperties, - CoreProperties coreProperties, + int corePropertiesAndEmojiProperties, int[] crossRerefences ) { @@ -67,10 +64,10 @@ namespace System.Unicode this.SimpleLowerCaseMapping = simpleLowerCaseMapping; this.SimpleTitleCaseMapping = simpleTitleCaseMapping; this.ContributoryProperties = contributoryProperties; - this.CoreProperties = coreProperties; + this.corePropertiesAndEmojiProperties = corePropertiesAndEmojiProperties; this.CrossRerefences = crossRerefences; } - public UnicodeRationalNumber? NumericValue { get { return NumericType != UnicodeNumericType.None ? numericValue : null as UnicodeRationalNumber?; } } + public UnicodeRationalNumber? NumericValue => NumericType != UnicodeNumericType.None ? numericValue : null as UnicodeRationalNumber?; } } diff --git a/UnicodeInformation/UnicodeInfo.cs b/UnicodeInformation/UnicodeInfo.cs index 564d367..56798d7 100644 --- a/UnicodeInformation/UnicodeInfo.cs +++ b/UnicodeInformation/UnicodeInfo.cs @@ -13,6 +13,11 @@ namespace System.Unicode /// Provides access to unicode information. public static class UnicodeInfo { + // NB: These fields will be used as a default value for UnicodeCharacterData and UnihanCharacterData, and passed by reference. + // They should be considered as being redonly, even if they aren't explicitely. + private static /*readonly*/ UnicodeCharacterData DefaultUnicodeCharacterData = new UnicodeCharacterData(default(UnicodeCodePointRange), null, null, UnicodeCategory.OtherNotAssigned, 0, 0, 0, null, 0, default(UnicodeRationalNumber), false, null, null, null, null, 0, 0, null); + private static /*readonly*/ UnihanCharacterData DefaultUnihanCharacterData = default(UnihanCharacterData); + /// The block name returned when no block is assigned to a specific code point. public const string DefaultBlock = "No_Block"; @@ -47,14 +52,18 @@ namespace System.Unicode if (formatVersion != 2) throw new InvalidDataException(); var fileUnicodeVersion = new Version(reader.ReadUInt16(), reader.ReadByte(), reader.ReadByte()); - - var unicodeCharacterDataEntries = new UnicodeCharacterData[ReadCodePoint(reader)]; + + var unicodeCharacterDataEntries = new UnicodeCharacterData[ReadCodePoint(reader)]; // Allocate one extra entry to act as a dummy entry. byte[] nameBuffer = new byte[128]; int mci = 0; for (i = 0; i < unicodeCharacterDataEntries.Length; ++i) { - if ((unicodeCharacterDataEntries[i] = ReadUnicodeCharacterDataEntry(reader, nameBuffer)).CodePointRange.Contains(i)) mci = i; + ReadUnicodeCharacterDataEntry(reader, nameBuffer, out unicodeCharacterDataEntries[i]); + if (unicodeCharacterDataEntries[i].CodePointRange.Contains(i)) + { + mci = i; + } else { ++i; @@ -66,7 +75,7 @@ namespace System.Unicode for (; i < unicodeCharacterDataEntries.Length; ++i) { - unicodeCharacterDataEntries[i] = ReadUnicodeCharacterDataEntry(reader, nameBuffer); + ReadUnicodeCharacterDataEntry(reader, nameBuffer, out unicodeCharacterDataEntries[i]); } var blockEntries = new UnicodeBlock[reader.ReadUInt16()]; @@ -87,7 +96,7 @@ namespace System.Unicode for (i = 0; i < unihanCharacterDataEntries.Length; ++i) { - unihanCharacterDataEntries[i] = ReadUnihanCharacterDataEntry(reader); + ReadUnihanCharacterDataEntry(reader, out unihanCharacterDataEntries[i]); } unicodeVersion = fileUnicodeVersion; @@ -98,7 +107,7 @@ namespace System.Unicode } } - private static UnicodeCharacterData ReadUnicodeCharacterDataEntry(BinaryReader reader, byte[] nameBuffer) + private static void ReadUnicodeCharacterDataEntry(BinaryReader reader, byte[] nameBuffer, out UnicodeCharacterData value) { var fields = (UcdFields)reader.ReadUInt16(); @@ -153,7 +162,7 @@ namespace System.Unicode string simpleLowerCaseMapping = (fields & UcdFields.SimpleLowerCaseMapping) != 0 ? reader.ReadString() : null; string simpleTitleCaseMapping = (fields & UcdFields.SimpleTitleCaseMapping) != 0 ? reader.ReadString() : null; ContributoryProperties contributoryProperties = (fields & UcdFields.ContributoryProperties) != 0 ? (ContributoryProperties)reader.ReadInt32() : 0; - CoreProperties coreProperties = (fields & UcdFields.CoreProperties) != 0 ? (CoreProperties)ReadInt24(reader) : 0; + int corePropertiesAndEmojiProperties = (fields & UcdFields.CorePropertiesAndEmojiProperties) != 0 ? ReadInt24(reader) : 0; int[] crossReferences = (fields & UcdFields.CrossRerefences) != 0 ? new int[reader.ReadByte() + 1] : null; if (crossReferences != null) @@ -162,7 +171,7 @@ namespace System.Unicode crossReferences[i] = ReadCodePoint(reader); } - return new UnicodeCharacterData + value = new UnicodeCharacterData ( codePointRange, name, @@ -180,12 +189,12 @@ namespace System.Unicode simpleLowerCaseMapping, simpleTitleCaseMapping, contributoryProperties, - coreProperties, + corePropertiesAndEmojiProperties, crossReferences ); } - private static UnihanCharacterData ReadUnihanCharacterDataEntry(BinaryReader reader) + private static void ReadUnihanCharacterDataEntry(BinaryReader reader, out UnihanCharacterData value) { var fields = (UnihanFields)reader.ReadUInt16(); @@ -219,7 +228,7 @@ namespace System.Unicode string simplifiedVariant = (fields & UnihanFields.SimplifiedVariant) != 0 ? reader.ReadString() : null; string traditionalVariant = (fields & UnihanFields.TraditionalVariant) != 0 ? reader.ReadString() : null; - return new UnihanCharacterData + value = new UnihanCharacterData ( codePoint, numericType, @@ -250,14 +259,9 @@ namespace System.Unicode } private static UnicodeBlock ReadBlockEntry(BinaryReader reader) - { - return new UnicodeBlock(new UnicodeCodePointRange(ReadCodePoint(reader), ReadCodePoint(reader)), reader.ReadString()); - } + => new UnicodeBlock(new UnicodeCodePointRange(ReadCodePoint(reader), ReadCodePoint(reader)), reader.ReadString()); - private static int ReadInt24(BinaryReader reader) - { - return reader.ReadByte() | ((reader.ReadByte() | (reader.ReadByte() << 8)) << 8); - } + private static int ReadInt24(BinaryReader reader) => reader.ReadByte() | ((reader.ReadByte() | (reader.ReadByte() << 8)) << 8); #if DEBUG internal static int ReadCodePoint(BinaryReader reader) @@ -285,21 +289,12 @@ namespace System.Unicode /// Gets the version of the Unicode standard supported by the class. public static Version UnicodeVersion { get { return unicodeVersion; } } - private static UnicodeCharacterData FindUnicodeCodePoint(int codePoint) - { - // For the first code points (this includes all of ASCII, and quite a bit more), the index in the table will be the code point itself. - if (codePoint <= maxContiguousIndex) - { - return unicodeCharacterData[codePoint]; - } - else - { - // For other code points, we will use a classic binary search with adjusted search indexes. - return BinarySearchUnicodeCodePoint(codePoint); - } - } + private static int FindUnicodeCodePointIndex(int codePoint) + => codePoint <= maxContiguousIndex ? + codePoint : // For the first code points (this includes all of ASCII, and quite a bit more), the index in the table will be the code point itself. + BinarySearchUnicodeCodePointIndex(codePoint); // For other code points, we will use a classic binary search with adjusted search indexes. - private static UnicodeCharacterData BinarySearchUnicodeCodePoint(int codePoint) + private static int BinarySearchUnicodeCodePointIndex(int codePoint) { // NB: Due to the strictly ordered nature of the table, we know that a code point can never happen after the index which is the code point itself. // This will greatly reduce the range to scan for characters close to maxContiguousIndex, and will have a lesser impact on other characters. @@ -312,22 +307,22 @@ namespace System.Unicode int Δ = unicodeCharacterData[index].CodePointRange.CompareCodePoint(codePoint); - if (Δ == 0) return unicodeCharacterData[index]; + if (Δ == 0) return index; else if (Δ < 0) maxIndex = index - 1; else minIndex = index + 1; } while (minIndex <= maxIndex); - return null; + return -1; } - private static UnihanCharacterData FindUnihanCodePoint(int codePoint) + private static int FindUnihanCodePointIndex(int codePoint) { int minIndex; int maxIndex; if (unihanCharacterData.Length == 0 || codePoint < unihanCharacterData[minIndex = 0].CodePoint || codePoint > unihanCharacterData[maxIndex = unihanCharacterData.Length - 1].CodePoint) { - return null; + return -1; } do @@ -336,12 +331,12 @@ namespace System.Unicode int Δ = codePoint - unihanCharacterData[index].CodePoint; - if (Δ == 0) return unihanCharacterData[index]; + if (Δ == 0) return index; else if (Δ < 0) maxIndex = index - 1; else minIndex = index + 1; } while (minIndex <= maxIndex); - return null; + return -1; } private static int FindBlockIndex(int codePoint) @@ -363,16 +358,26 @@ namespace System.Unicode return -1; } + internal static ref UnicodeCharacterData GetUnicodeCharacterData(int index) + { + if (index >= 0) return ref unicodeCharacterData[index]; + return ref DefaultUnicodeCharacterData; + } + + internal static ref UnihanCharacterData GetUnihanCharacterData(int index) + { + if (index >= 0) return ref unihanCharacterData[index]; + return ref DefaultUnihanCharacterData; + } + /// Gets the name of the Unicode block containing the character. /// If the character has not been assigned to a block, the value of will be returned. /// The Unicode code point whose block should be retrieved. /// The name of the block the code point was assigned to. public static string GetBlockName(int codePoint) - { - int i = FindBlockIndex(codePoint); - - return i >= 0 ? blocks[i].Name : DefaultBlock; - } + => FindBlockIndex(codePoint) is int i && i >= 0 ? + blocks[i].Name : + DefaultBlock; /// Gets Unicode information on the specified code point. /// @@ -400,9 +405,7 @@ namespace System.Unicode /// The Unicode code point for which the data must be retrieved. /// The name of the code point, if defined; otherwise. public static UnicodeCharInfo GetCharInfo(int codePoint) - { - return new UnicodeCharInfo(codePoint, FindUnicodeCodePoint(codePoint), FindUnihanCodePoint(codePoint), GetBlockName(codePoint)); - } + => new UnicodeCharInfo(codePoint, FindUnicodeCodePointIndex(codePoint), FindUnihanCodePointIndex(codePoint), GetBlockName(codePoint)); /// Gets the category of the specified code point. /// @@ -412,11 +415,9 @@ namespace System.Unicode /// The Unicode code point for which the category must be retrieved. /// The category of the code point. public static UnicodeCategory GetCategory(int codePoint) - { - var charData = FindUnicodeCodePoint(codePoint); - - return charData != null ? charData.Category : UnicodeCategory.OtherNotAssigned; - } + => FindUnicodeCodePointIndex(codePoint) is int unicodeCharacterDataIndex && unicodeCharacterDataIndex >= 0 ? + GetUnicodeCharacterData(unicodeCharacterDataIndex).Category : + UnicodeCategory.OtherNotAssigned; /// Gets a display text for the specified code point. /// The information for the code point. @@ -448,19 +449,16 @@ namespace System.Unicode /// The Unicode code point for which the name must be retrieved. /// The name of the code point, if defined; otherwise. public static string GetName(int codePoint) - { - if (HangulInfo.IsHangul(codePoint)) return HangulInfo.GetHangulName((char)codePoint); - else return GetNameInternal(codePoint); - } + => HangulInfo.IsHangul(codePoint) ? + HangulInfo.GetHangulName((char)codePoint) : + GetNameInternal(codePoint); private static string GetNameInternal(int codePoint) - { - var codePointInfo = FindUnicodeCodePoint(codePoint); + => FindUnicodeCodePointIndex(codePoint) is int codePointInfoIndex && codePointInfoIndex >= 0 ? + GetName(codePoint, ref GetUnicodeCharacterData(codePointInfoIndex)) : + null; - return codePointInfo != null ? GetName(codePoint, codePointInfo) : null; - } - - internal static string GetName(int codePoint, UnicodeCharacterData characterData) + internal static string GetName(int codePoint, ref UnicodeCharacterData characterData) { if (characterData.CodePointRange.IsSingleCodePoint) return characterData.Name; else if (HangulInfo.IsHangul(codePoint)) return HangulInfo.GetHangulName((char)codePoint); @@ -473,20 +471,15 @@ namespace System.Unicode /// Information on the specified radical. /// The parameter is out of range. public static CjkRadicalInfo GetCjkRadicalInfo(int radicalIndex) - { - return new CjkRadicalInfo(checked((byte)radicalIndex), radicals[radicalIndex - 1]); - } + => new CjkRadicalInfo(checked((byte)radicalIndex), radicals[radicalIndex - 1]); /// Returns the number of CJK radicals in the Unicode data. /// This value will be 214 for the foreseeable future. - public static int CjkRadicalCount { get { return radicals.Length; } } + public static int CjkRadicalCount => radicals.Length; /// Gets all the blocks defined in the Unicode data. /// is not the name of a block, but only a value indicating the abscence of block information for a given code point. /// An array containing an entry for every block defined in the Unicode data. - public static UnicodeBlock[] GetBlocks() - { - return (UnicodeBlock[])blocks.Clone(); - } + public static UnicodeBlock[] GetBlocks() => (UnicodeBlock[])blocks.Clone(); } } diff --git a/UnicodeInformation/UnihanCharacterData.cs b/UnicodeInformation/UnihanCharacterData.cs index 5ae538e..e2f3cb5 100644 --- a/UnicodeInformation/UnihanCharacterData.cs +++ b/UnicodeInformation/UnihanCharacterData.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace System.Unicode +namespace System.Unicode { - internal sealed class UnihanCharacterData + internal struct UnihanCharacterData { public readonly int CodePoint; public readonly UnihanNumericType NumericType;