|
2 | 2 | // Copyright 2013 Andreas Gullberg Larsen ([email protected]). Maintained at https://github.com/angularsen/UnitsNet.
|
3 | 3 |
|
4 | 4 | using System;
|
| 5 | +using System.Collections.Concurrent; |
5 | 6 | using System.Collections.Generic;
|
6 | 7 | using System.Linq;
|
7 | 8 |
|
8 |
| -using UnitToAbbreviationMap = System.Collections.Generic.Dictionary<int, System.Collections.Generic.List<string>>; |
9 |
| -using AbbreviationToUnitMap = System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<int>>; |
10 |
| - |
11 | 9 | namespace UnitsNet
|
12 | 10 | {
|
| 11 | + internal class UnitToAbbreviationMap : ConcurrentDictionary<int, IReadOnlyList<string>> { } |
| 12 | + internal class AbbreviationToUnitMap : ConcurrentDictionary<string, IReadOnlyList<int>> { } |
| 13 | + |
13 | 14 | internal class UnitValueAbbreviationLookup
|
14 | 15 | {
|
15 | 16 | private readonly UnitToAbbreviationMap _unitToAbbreviationMap = new();
|
16 | 17 | private readonly AbbreviationToUnitMap _abbreviationToUnitMap = new();
|
17 | 18 | private readonly AbbreviationToUnitMap _lowerCaseAbbreviationToUnitMap = new();
|
| 19 | + private Lazy<string[]> _allUnitAbbreviationsLazy; |
| 20 | + private readonly object _syncRoot = new(); |
18 | 21 |
|
19 |
| - internal string[] GetAllUnitAbbreviationsForQuantity() |
| 22 | + public UnitValueAbbreviationLookup() |
20 | 23 | {
|
21 |
| - return _unitToAbbreviationMap.Values.SelectMany(abbreviations => abbreviations).Distinct().ToArray(); |
| 24 | + _allUnitAbbreviationsLazy = new Lazy<string[]>(ComputeAllUnitAbbreviationsValue); |
| 25 | + } |
| 26 | + |
| 27 | + internal IReadOnlyList<string> GetAllUnitAbbreviationsForQuantity() |
| 28 | + { |
| 29 | + return _allUnitAbbreviationsLazy.Value; |
22 | 30 | }
|
23 | 31 |
|
24 |
| - internal List<string> GetAbbreviationsForUnit<TUnitType>(TUnitType unit) where TUnitType : Enum |
| 32 | + internal IReadOnlyList<string> GetAbbreviationsForUnit<TUnitType>(TUnitType unit) where TUnitType : Enum |
25 | 33 | {
|
26 | 34 | return GetAbbreviationsForUnit(Convert.ToInt32(unit));
|
27 | 35 | }
|
28 | 36 |
|
29 |
| - internal List<string> GetAbbreviationsForUnit(int unit) |
| 37 | + internal IReadOnlyList<string> GetAbbreviationsForUnit(int unit) |
30 | 38 | {
|
31 | 39 | if (!_unitToAbbreviationMap.TryGetValue(unit, out var abbreviations))
|
32 | 40 | return new List<string>(0);
|
33 | 41 |
|
34 |
| - return abbreviations.Distinct().ToList(); |
| 42 | + return abbreviations.ToList(); |
35 | 43 | }
|
36 | 44 |
|
37 |
| - internal List<int> GetUnitsForAbbreviation(string abbreviation, bool ignoreCase) |
| 45 | + internal IReadOnlyList<int> GetUnitsForAbbreviation(string abbreviation, bool ignoreCase) |
38 | 46 | {
|
39 | 47 | var lowerCaseAbbreviation = abbreviation.ToLower();
|
40 | 48 | var key = ignoreCase ? lowerCaseAbbreviation : abbreviation;
|
41 | 49 | var map = ignoreCase ? _lowerCaseAbbreviationToUnitMap : _abbreviationToUnitMap;
|
42 | 50 |
|
43 |
| - if (!map.TryGetValue(key, out List<int> units)) |
| 51 | + if (!map.TryGetValue(key, out IReadOnlyList<int> units)) |
44 | 52 | return new List<int>(0);
|
45 | 53 |
|
46 |
| - return units.Distinct().ToList(); |
| 54 | + return units.ToList(); |
47 | 55 | }
|
48 | 56 |
|
49 | 57 | internal void Add(int unit, string abbreviation, bool setAsDefault = false, bool allowAbbreviationLookup = true)
|
50 | 58 | {
|
51 |
| - var lowerCaseAbbreviation = abbreviation.ToLower(); |
52 |
| - |
53 |
| - if (!_unitToAbbreviationMap.TryGetValue(unit, out var abbreviationsForUnit)) |
54 |
| - abbreviationsForUnit = _unitToAbbreviationMap[unit] = new List<string>(); |
55 |
| - |
56 |
| - if (allowAbbreviationLookup) |
| 59 | + // Restrict concurrency on writes. |
| 60 | + // By using ConcurrencyDictionary and immutable IReadOnlyList instances, we don't need to lock on reads. |
| 61 | + lock (_syncRoot) |
57 | 62 | {
|
58 |
| - if (!_abbreviationToUnitMap.TryGetValue(abbreviation, out var unitsForAbbreviation)) |
59 |
| - _abbreviationToUnitMap[abbreviation] = unitsForAbbreviation = new List<int>(); |
60 |
| - |
61 |
| - if (!_lowerCaseAbbreviationToUnitMap.TryGetValue(lowerCaseAbbreviation, out var unitsForLowerCaseAbbreviation)) |
62 |
| - _lowerCaseAbbreviationToUnitMap[lowerCaseAbbreviation] = unitsForLowerCaseAbbreviation = new List<int>(); |
63 |
| - |
64 |
| - unitsForLowerCaseAbbreviation.Remove(unit); |
65 |
| - unitsForLowerCaseAbbreviation.Add(unit); |
66 |
| - |
67 |
| - unitsForAbbreviation.Remove(unit); |
68 |
| - unitsForAbbreviation.Add(unit); |
| 63 | + var lowerCaseAbbreviation = abbreviation.ToLower(); |
| 64 | + |
| 65 | + if (allowAbbreviationLookup) |
| 66 | + { |
| 67 | + _abbreviationToUnitMap.AddOrUpdate(abbreviation, |
| 68 | + addValueFactory: _ => new List<int> { unit }, |
| 69 | + updateValueFactory: (_, existing) => existing.Append(unit).Distinct().ToList()); |
| 70 | + |
| 71 | + _lowerCaseAbbreviationToUnitMap.AddOrUpdate(lowerCaseAbbreviation, |
| 72 | + addValueFactory: _ => new List<int> { unit }, |
| 73 | + updateValueFactory: (_, existing) => existing.Append(unit).Distinct().ToList()); |
| 74 | + } |
| 75 | + |
| 76 | + _unitToAbbreviationMap.AddOrUpdate(unit, |
| 77 | + addValueFactory: _ => new List<string> { abbreviation }, |
| 78 | + updateValueFactory: (_, existing) => |
| 79 | + { |
| 80 | + return setAsDefault |
| 81 | + ? existing.Where(x => x != abbreviation).Prepend(abbreviation).ToList() |
| 82 | + : existing.Where(x => x != abbreviation).Append(abbreviation).ToList(); |
| 83 | + }); |
| 84 | + |
| 85 | + _allUnitAbbreviationsLazy = new Lazy<string[]>(ComputeAllUnitAbbreviationsValue); |
69 | 86 | }
|
| 87 | + } |
70 | 88 |
|
71 |
| - abbreviationsForUnit.Remove(abbreviation); |
72 |
| - |
73 |
| - if (setAsDefault) |
74 |
| - abbreviationsForUnit.Insert(0, abbreviation); |
75 |
| - else |
76 |
| - abbreviationsForUnit.Add(abbreviation); |
| 89 | + private string[] ComputeAllUnitAbbreviationsValue() |
| 90 | + { |
| 91 | + return _unitToAbbreviationMap.Values.SelectMany(abbreviations => abbreviations).Distinct().ToArray(); |
77 | 92 | }
|
78 | 93 | }
|
79 | 94 | }
|
0 commit comments