Skip to content

Commit 52beb2b

Browse files
Added a first pass of pseudo code for looking up cursors from a collection of pages
1 parent 438e510 commit 52beb2b

File tree

3 files changed

+67
-1
lines changed

3 files changed

+67
-1
lines changed

Sources/CodableDatastore/Persistence/DatastoreInterfaceError.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ public enum DatastoreInterfaceError: LocalizedError {
2828
/// The requested insertion cursor conflicts with an already existing identifier.
2929
case instanceAlreadyExists
3030

31+
/// The datastore being manipulated does not yet exist in the persistence.
32+
case datastoreKeyNotFound
33+
3134
public var errorDescription: String? {
3235
switch self {
3336
case .multipleRegistrations:
@@ -42,6 +45,8 @@ public enum DatastoreInterfaceError: LocalizedError {
4245
return "The requested instance could not be found with the specified identifier."
4346
case .instanceAlreadyExists:
4447
return "The requested insertion cursor conflicts with an already existing identifier."
48+
case .datastoreKeyNotFound:
49+
return "The datastore being manipulated does not yet exist in the persistence."
4550
}
4651
}
4752
}

Sources/CodableDatastore/Persistence/Disk Persistence/Datastore/DatastorePage.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ extension DiskPersistence.Datastore.Page {
9494
}
9595
}
9696

97-
private var blocks: MultiplexedAsyncSequence<AnyReadableSequence<DatastorePageEntryBlock>> {
97+
var blocks: MultiplexedAsyncSequence<AnyReadableSequence<DatastorePageEntryBlock>> {
9898
get async throws {
9999
if let blocksReaderTask {
100100
return try await blocksReaderTask.value

Sources/CodableDatastore/Persistence/Disk Persistence/Transaction/Transaction.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//
88

99
import Foundation
10+
import Bytes
1011

1112
extension DiskPersistence {
1213
actor Transaction: AnyDiskTransaction {
@@ -294,6 +295,66 @@ extension DiskPersistence.Transaction {
294295
instanceData: Data,
295296
versionData: Data
296297
) {
298+
299+
guard let rootObject = try await rootObject(for: datastoreKey)
300+
else { throw DatastoreInterfaceError.datastoreKeyNotFound }
301+
302+
let index = try await rootObject.primaryIndex
303+
304+
let pages = try await index.orderedPages
305+
306+
// See https://stackoverflow.com/questions/26678362/how-do-i-insert-an-element-at-the-correct-position-into-a-sorted-array-in-swift/70645571#70645571
307+
// guard !pages.isEmpty else { return Cursor(index: index, after: nil)
308+
309+
var bytesForFirstEntry: Bytes?
310+
311+
let middle = pages.count/2
312+
pageIterator: for page in pages[middle...] {
313+
let blocks = try await page.blocks
314+
315+
for try await block in blocks {
316+
switch block {
317+
case .complete(let bytes):
318+
/// We have a complete entry, lets use it and stop scanning
319+
bytesForFirstEntry = bytes
320+
break pageIterator
321+
case .head(let bytes):
322+
/// We are starting an entry, but will need to go to the next page.
323+
bytesForFirstEntry = bytes
324+
case .slice(let bytes):
325+
/// In the first position, lets skip it.
326+
guard bytesForFirstEntry != nil else { continue }
327+
/// In the final position, lets save and continue.
328+
bytesForFirstEntry?.append(contentsOf: bytes)
329+
case .tail(let bytes):
330+
/// In the first position, lets skip it.
331+
guard bytesForFirstEntry != nil else { continue }
332+
/// In the final position, lets save and stop.
333+
bytesForFirstEntry?.append(contentsOf: bytes)
334+
break pageIterator
335+
}
336+
}
337+
}
338+
339+
// let entry = try DatastorePageEntry(bytes: bytes)
340+
let firstEntry = DatastorePageEntry(headers: [], content: [])
341+
// guard entry.headers.count == 2 else { throw cannot decode page }
342+
// let versionBytes = firstEntry.headers[0]
343+
let identifierBytes = firstEntry.headers[1]
344+
345+
let firstEntryIdentifier = try JSONDecoder.shared.decode(IdentifierType.self, from: Data(identifierBytes))
346+
347+
if firstEntryIdentifier < identifier {
348+
// evaluate [index(after: middle)...]
349+
} else {
350+
// evaluate [..<middle]
351+
}
352+
// end of page search
353+
354+
// We have the index we could insert the entry as a new page, but it could be located either on the previous page of the specified one, so check the previous page and check every entry from there, continuing to subsequent pages from there.
355+
// Cursor should include the page _index_ as a hint, as the structure may change between loading the index and writing to it.
356+
357+
297358
preconditionFailure("Unimplemented")
298359
}
299360

0 commit comments

Comments
 (0)