@@ -35,6 +35,12 @@ internal final class InternalDefaultLiveMap: Sendable {
3535 }
3636 }
3737
38+ internal var testsOnly_clearTimeserial : String ? {
39+ mutableStateMutex. withSync { mutableState in
40+ mutableState. clearTimeserial
41+ }
42+ }
43+
3844 private let logger : Logger
3945 private let userCallbackQueue : DispatchQueue
4046 private let clock : SimpleClock
@@ -396,6 +402,15 @@ internal final class InternalDefaultLiveMap: Sendable {
396402 }
397403 }
398404
405+ /// Test-only method to apply a MAP_CLEAR operation, per RTLM24.
406+ internal func testsOnly_applyMapClearOperation( serial: String ? ) -> LiveObjectUpdate < DefaultLiveMapUpdate > {
407+ mutableStateMutex. withSync { mutableState in
408+ mutableState. applyMapClearOperation (
409+ serial: serial,
410+ )
411+ }
412+ }
413+
399414 /// Resets the map's data, per RTO4b2. This is to be used when an `ATTACHED` ProtocolMessage indicates that the only object in a channel is an empty root map.
400415 internal func nosync_resetData( ) {
401416 mutableStateMutex. withoutSync { mutableState in
@@ -452,6 +467,9 @@ internal final class InternalDefaultLiveMap: Sendable {
452467 /// The "private `semantics` field" of RTO5c1b1b.
453468 internal var semantics : WireEnum < ObjectsMapSemantics > ?
454469
470+ /// RTLM25
471+ internal var clearTimeserial : String ?
472+
455473 /// Replaces the internal data of this map with the provided ObjectState, per RTLM6.
456474 ///
457475 /// - Parameters:
@@ -492,6 +510,9 @@ internal final class InternalDefaultLiveMap: Sendable {
492510 // RTLM6g: Store the current data value as previousData for use in RTLM6h
493511 let previousData = data
494512
513+ // RTLM6i
514+ clearTimeserial = state. map? . clearTimeserial
515+
495516 // RTLM6b: Set the private flag createOperationIsMerged to false
496517 liveObjectMutableState. createOperationIsMerged = false
497518
@@ -702,6 +723,15 @@ internal final class InternalDefaultLiveMap: Sendable {
702723 liveObjectMutableState. emit ( . update( . init( update: dataBeforeApplyingOperation. mapValues { _ in . removed } ) ) , on: userCallbackQueue)
703724 // RTLM15d5b
704725 return true
726+ case . known( . mapClear) :
727+ // RTLM15d8
728+ let update = applyMapClearOperation (
729+ serial: applicableOperation. objectMessageSerial,
730+ )
731+ // RTLM15d8a
732+ liveObjectMutableState. emit ( update, on: userCallbackQueue)
733+ // RTLM15d8b
734+ return true
705735 default :
706736 // RTLM15d4
707737 logger. log ( " Operation \( operation) has unsupported action for LiveMap; discarding " , level: . warn)
@@ -720,6 +750,11 @@ internal final class InternalDefaultLiveMap: Sendable {
720750 userCallbackQueue: DispatchQueue ,
721751 clock: SimpleClock ,
722752 ) -> LiveObjectUpdate < DefaultLiveMapUpdate > {
753+ // RTLM7h
754+ if let clearTimeserial, operationTimeserial. map ( { $0 <= clearTimeserial } ) ?? true {
755+ return . noop
756+ }
757+
723758 // RTLM7a: If an entry exists in the private data for the specified key
724759 if let existingEntry = data [ key] {
725760 // RTLM7a1: If the operation cannot be applied as per RTLM9, discard the operation
@@ -762,6 +797,11 @@ internal final class InternalDefaultLiveMap: Sendable {
762797 internal mutating func applyMapRemoveOperation( key: String , operationTimeserial: String ? , operationSerialTimestamp: Date ? , logger: Logger , clock: SimpleClock ) -> LiveObjectUpdate < DefaultLiveMapUpdate > {
763798 // (Note that, where the spec tells us to set ObjectsMapEntry.data to nil, we actually set it to an empty ObjectData, which is equivalent, since it contains no data)
764799
800+ // RTLM8g
801+ if let clearTimeserial, operationTimeserial. map ( { $0 <= clearTimeserial } ) ?? true {
802+ return . noop
803+ }
804+
765805 // Calculate the tombstonedAt for the new or updated entry per RTLM8f
766806 let tombstonedAt : Date ?
767807 if let operationSerialTimestamp {
@@ -869,11 +909,44 @@ internal final class InternalDefaultLiveMap: Sendable {
869909 )
870910 }
871911
912+ /// Applies a `MAP_CLEAR` operation, per RTLM24.
913+ internal mutating func applyMapClearOperation(
914+ serial: String ? ,
915+ ) -> LiveObjectUpdate < DefaultLiveMapUpdate > {
916+ guard let serial else {
917+ return . noop
918+ }
919+
920+ // RTLM24c
921+ if let clearTimeserial, serial <= clearTimeserial {
922+ return . noop
923+ }
924+
925+ // RTLM24d
926+ clearTimeserial = serial
927+
928+ // RTLM24e, RTLM24e1: entry timeserial is nil, or serial > entry timeserial
929+ let keysToRemove = data. filter { _, entry in
930+ guard let entryTimeserial = entry. timeserial else {
931+ return true
932+ }
933+ return serial > entryTimeserial
934+ } . keys
935+
936+ for key in keysToRemove {
937+ data. removeValue ( forKey: key)
938+ }
939+
940+ // RTLM24e1b, RTLM24f
941+ let removedKeys = Dictionary ( uniqueKeysWithValues: keysToRemove. map { ( $0, LiveMapUpdateAction . removed) } )
942+ return . update( DefaultLiveMapUpdate ( update: removedKeys) )
943+ }
944+
872945 /// Resets the map's data and emits a `removed` event for the existing keys, per RTO4b2 and RTO4b2a. This is to be used when an `ATTACHED` ProtocolMessage indicates that the only object in a channel is an empty root map.
873946 internal mutating func resetData( userCallbackQueue: DispatchQueue ) {
874947 // RTO4b2
875948 let previousData = data
876- data = [ : ]
949+ resetDataToZeroValued ( )
877950
878951 // RTO4b2a
879952 let mapUpdate = DefaultLiveMapUpdate ( update: previousData. mapValues { _ in . removed } )
@@ -884,6 +957,7 @@ internal final class InternalDefaultLiveMap: Sendable {
884957 mutating func resetDataToZeroValued( ) {
885958 // RTLM4
886959 data = [ : ]
960+ clearTimeserial = nil
887961 }
888962
889963 /// Releases entries that were tombstoned more than `gracePeriod` ago, per RTLM19.
0 commit comments