From 162113172022a3283247af5af5c941a79a867719 Mon Sep 17 00:00:00 2001 From: AKosylo Date: Wed, 11 Oct 2023 14:12:41 +0200 Subject: [PATCH] OrderSource: WIP --- .../Events/Market/Extra/OrderBase.swift | 7 ++-- .../Events/Market/Extra/OrderSource.swift | 21 ++++------- .../Events/Market/Extra/PriceType.swift | 2 +- DXFeedFramework/Events/Market/Greeks.swift | 4 +-- .../Native/Events/Markets/TheoPrice+Ext.swift | 2 +- DXFeedFramework/Utils/ConcurrentDict.swift | 18 ++++++---- DXFeedFrameworkTests/OrderSourceTest.swift | 36 ++++++++++++++++++- 7 files changed, 60 insertions(+), 30 deletions(-) diff --git a/DXFeedFramework/Events/Market/Extra/OrderBase.swift b/DXFeedFramework/Events/Market/Extra/OrderBase.swift index 3398dc0dc..60f8b040a 100644 --- a/DXFeedFramework/Events/Market/Extra/OrderBase.swift +++ b/DXFeedFramework/Events/Market/Extra/OrderBase.swift @@ -9,7 +9,7 @@ import Foundation class OrderBase: MarketEvent, IIndexedEvent, CustomStringConvertible { var eventSource: IndexedEventSource = .defaultSource - + var type: EventCode = .orderBase // var eventSource: IndexedEventSource { @@ -113,7 +113,7 @@ class OrderBase: MarketEvent, IIndexedEvent, CustomStringConvertible { public var tradePrice: Double = .nan /// Gets or sets trade size for events containing trade-related action. public var tradeSize: Double = .nan - + init(_ eventSymbol: String) { self.eventSymbol = eventSymbol } @@ -140,9 +140,8 @@ tradePrice: \(tradePrice), \ tradeSize: \(tradeSize) """ } - -} +} extension OrderBase { /// Gets a value indicating whether this order has some size diff --git a/DXFeedFramework/Events/Market/Extra/OrderSource.swift b/DXFeedFramework/Events/Market/Extra/OrderSource.swift index d95fa5d36..0d2cf923d 100644 --- a/DXFeedFramework/Events/Market/Extra/OrderSource.swift +++ b/DXFeedFramework/Events/Market/Extra/OrderSource.swift @@ -37,13 +37,13 @@ public class OrderSource: IndexedEventSource { static private let sourcesByName = ConcurrentDict() private let sourcesByIdCache = NSCache() - private override init(identifier: Int, name: String) { + override init(identifier: Int, name: String) { self.pubFlags = 0 self.isBuiltin = false super.init(identifier: identifier, name: name) } - private init(identifier: Int, name: String, pubFlags: Int) throws { + init(identifier: Int, name: String, pubFlags: Int) throws { self.pubFlags = pubFlags self.isBuiltin = true super.init(identifier: identifier, name: name) @@ -74,7 +74,7 @@ public class OrderSource: IndexedEventSource { } /// Determines whether specified source identifier refers to special order source. - ///Special order sources are used for wrapping non-order events into order events. + /// Special order sources are used for wrapping non-order events into order events. internal static func isSpecialSourceId(sourceId: Int) -> Bool { return sourceId >= 1 && sourceId <= 6 } @@ -88,7 +88,7 @@ public class OrderSource: IndexedEventSource { for index in 0.. OrderSource { - var source: OrderSource - if let source = sourcesById[identifier] { - return source - } else { + return try OrderSource.sourcesById.tryGetValue(key: identifier) { let name = try decodeName(identifier: identifier) let source = OrderSource(identifier: identifier, name: name) - sourcesById[identifier] = source return source } } @@ -139,16 +135,11 @@ public class OrderSource: IndexedEventSource { /// The name must be either predefined, or contain at most 4 alphanumeric characters. /// - Throws: ``ArgumentException/exception(_:)``. Rethrows exception from Java. public static func valueOf(name: String) throws -> OrderSource { - var source: OrderSource - if let source = sourcesByName[name] { - return source - } else { + return try OrderSource.sourcesByName.tryGetValue(key: name) { let identifier = try composeId(name: name) let source = OrderSource(identifier: identifier, name: name) - sourcesByName[name] = source return source } - } } diff --git a/DXFeedFramework/Events/Market/Extra/PriceType.swift b/DXFeedFramework/Events/Market/Extra/PriceType.swift index 4368d6f29..cd659d321 100644 --- a/DXFeedFramework/Events/Market/Extra/PriceType.swift +++ b/DXFeedFramework/Events/Market/Extra/PriceType.swift @@ -45,7 +45,7 @@ public enum PriceType: DXPriceType, CaseIterable { EnumUtil.createEnumBitMaskArrayByValue(defaultValue: .regular, allCases: PriceType.allCases) }() - + public static func valueOf(_ value: Int) -> PriceType { return PriceType.types[value] } diff --git a/DXFeedFramework/Events/Market/Greeks.swift b/DXFeedFramework/Events/Market/Greeks.swift index 246d9a1d9..4ef003212 100644 --- a/DXFeedFramework/Events/Market/Greeks.swift +++ b/DXFeedFramework/Events/Market/Greeks.swift @@ -65,7 +65,7 @@ public class Greeks: MarketEvent, ITimeSeriesEvent, ILastingEvent, CustomStringC public init(_ eventSymbol: String) { self.eventSymbol = eventSymbol } - + public var description: String { """ DXFG_GREEKS_T \ @@ -96,7 +96,7 @@ extension Greeks { (Long(TimeUtil.getMillisFromTime(newValue)) << 22) | Int64(getSequence()) } - } + } /// Gets sequence number of this quote to distinguish events that have the same ``time``. /// This sequence number does not have to be unique and /// does not need to be sequential. Sequence can range from 0 to ``MarketEventConst/maxSequence``. diff --git a/DXFeedFramework/Native/Events/Markets/TheoPrice+Ext.swift b/DXFeedFramework/Native/Events/Markets/TheoPrice+Ext.swift index c2a0c240a..8321e898b 100644 --- a/DXFeedFramework/Native/Events/Markets/TheoPrice+Ext.swift +++ b/DXFeedFramework/Native/Events/Markets/TheoPrice+Ext.swift @@ -15,7 +15,7 @@ extension TheoPrice { self.eventTime = native.market_event.event_time self.eventFlags = native.event_flags self.index = native.index - + self.price = native.price self.underlyingPrice = native.underlying_price self.delta = native.delta diff --git a/DXFeedFramework/Utils/ConcurrentDict.swift b/DXFeedFramework/Utils/ConcurrentDict.swift index 473506073..a53be8259 100644 --- a/DXFeedFramework/Utils/ConcurrentDict.swift +++ b/DXFeedFramework/Utils/ConcurrentDict.swift @@ -59,14 +59,20 @@ class ConcurrentDict: CustomStringConvertible { } public func tryInsert(key: Key, value: Value) -> Bool { - var result = true - writer { - if $0[key] != nil { - result = false + return accessQueue.sync { + return set.updateValue(value, forKey: key) == nil + } + } + + public func tryGetValue(key: Key, generator: () throws -> Value) throws -> Value { + return try accessQueue.sync { + if let existingValue = set[key] { + return existingValue } - $0[key] = value + let newValue = try generator() + set[key] = newValue + return newValue } - return result } public func reader(_ block: ([Key: Value]) throws -> U) rethrows -> U { diff --git a/DXFeedFrameworkTests/OrderSourceTest.swift b/DXFeedFrameworkTests/OrderSourceTest.swift index 0b8c734be..26981cb67 100644 --- a/DXFeedFrameworkTests/OrderSourceTest.swift +++ b/DXFeedFrameworkTests/OrderSourceTest.swift @@ -18,7 +18,6 @@ final class OrderSourceTest: XCTestCase { // Put teardown code here. This method is called after the invocation of each test method in the class. } - func testIsSpecialSource() throws { XCTAssert(OrderSource.isSpecialSourceId(sourceId: 1)) XCTAssert(!OrderSource.isSpecialSourceId(sourceId: 10)) @@ -46,4 +45,39 @@ final class OrderSourceTest: XCTestCase { print("\(error)") } } + + func testCreateOrderSourceWithDuplicateName() throws { + do { + _ = try OrderSource(identifier: 44, name: "COMPOSITE_ASK", pubFlags: 0) + _ = try OrderSource(identifier: 33, name: "COMPOSITE_ASK", pubFlags: 0) + } catch ArgumentException.exception(let message) { + XCTAssert(message.contains("name"), "Wrong message \(message)") + return + } catch { + print("undefined error \(error)") + } + XCTAssert(false, "should be generated exception") + } + + func testCreateOrderSourceWithDuplicateId() throws { + do { + _ = try OrderSource(identifier: 3, name: "COMPOSITE_ASK1", pubFlags: 0) + _ = try OrderSource(identifier: 3, name: "COMPOSITE_ASK3", pubFlags: 0) + } catch ArgumentException.exception(let message) { + XCTAssert(message.contains("id"), "Wrong message \(message)") + return + } catch { + print("undefined error \(error)") + } + XCTAssert(false, "should be generated exception") + } + + func testCreateOrderSource() throws { + do { + _ = try OrderSource(identifier: 5, name: "COMPOSITE_ASK2", pubFlags: 0) + _ = try OrderSource(identifier: 6, name: "COMPOSITE_ASK4", pubFlags: 0) + } catch { + XCTAssert(false, "undefined error \(error)") + } + } }