Swiftgram/Postbox/MessageMediaTable.swift
2016-01-26 16:16:40 +03:00

210 lines
9.5 KiB
Swift

import Foundation
private enum MediaEntryType: Int8 {
case Direct
case MessageReference
}
enum InsertMediaResult {
case Reference
case Embed(Media)
}
enum DebugMediaEntry {
case Direct(Media, Int)
case MessageReference(MessageIndex)
}
final class MessageMediaTable {
let valueBox: ValueBox
let tableId: Int32
let mediaCleanupTable: MediaCleanupTable
init(valueBox: ValueBox, tableId: Int32, mediaCleanupTable: MediaCleanupTable) {
self.valueBox = valueBox
self.tableId = tableId
self.mediaCleanupTable = mediaCleanupTable
}
func key(id: MediaId, key: ValueBoxKey = ValueBoxKey(length: 4 + 8)) -> ValueBoxKey {
key.setInt32(0, value: id.namespace)
key.setInt64(4, value: id.id)
return key
}
func get(id: MediaId) -> Media? {
if let value = self.valueBox.get(self.tableId, key: self.key(id)) {
var type: Int8 = 0
value.read(&type, offset: 0, length: 1)
if type == MediaEntryType.Direct.rawValue {
var dataLength: Int32 = 0
value.read(&dataLength, offset: 0, length: 4)
if let media = Decoder(buffer: MemoryBuffer(memory: value.memory + value.offset, capacity: Int(dataLength), length: Int(dataLength), freeWhenDone: false)).decodeRootObject() as? Media {
return media
}
}
}
return nil
}
func set(media: Media, index: MessageIndex, messageHistoryTable: MessageHistoryTable, sharedWriteBuffer: WriteBuffer = WriteBuffer(), sharedEncoder: Encoder = Encoder()) -> InsertMediaResult {
if let id = media.id {
if let value = self.valueBox.get(self.tableId, key: self.key(id)) {
var type: Int8 = 0
value.read(&type, offset: 0, length: 1)
if type == MediaEntryType.Direct.rawValue {
var dataLength: Int32 = 0
value.read(&dataLength, offset: 0, length: 4)
value.skip(Int(dataLength))
sharedWriteBuffer.reset()
sharedWriteBuffer.write(value.memory, offset: 0, length: value.offset)
var messageReferenceCount: Int32 = 0
value.read(&messageReferenceCount, offset: 0, length: 4)
messageReferenceCount++
sharedWriteBuffer.write(&messageReferenceCount, offset: 0, length: 4)
self.valueBox.set(self.tableId, key: self.key(id), value: sharedWriteBuffer.readBufferNoCopy())
return .Reference
} else if type == MediaEntryType.MessageReference.rawValue {
var idPeerId: Int64 = 0
var idNamespace: Int32 = 0
var idId: Int32 = 0
var idTimestamp: Int32 = 0
value.read(&idPeerId, offset: 0, length: 8)
value.read(&idNamespace, offset: 0, length: 4)
value.read(&idId, offset: 0, length: 4)
value.read(&idTimestamp, offset: 0, length: 4)
let referencedMessageIndex = MessageIndex(id: MessageId(peerId: PeerId(idPeerId), namespace: idNamespace, id: idId), timestamp: idTimestamp)
if referencedMessageIndex == index {
return .Embed(media)
}
if let media = messageHistoryTable.unembedMedia(referencedMessageIndex, id: id) {
sharedWriteBuffer.reset()
var directType: Int8 = MediaEntryType.Direct.rawValue
sharedWriteBuffer.write(&directType, offset: 0, length: 1)
sharedEncoder.reset()
sharedEncoder.encodeRootObject(media)
let mediaBuffer = sharedEncoder.memoryBuffer()
var mediaBufferLength = Int32(mediaBuffer.length)
sharedWriteBuffer.write(&mediaBufferLength, offset: 0, length: 4)
sharedWriteBuffer.write(mediaBuffer.memory, offset: 0, length: mediaBuffer.length)
var messageReferenceCount: Int32 = 2
sharedWriteBuffer.write(&messageReferenceCount, offset: 0, length: 4)
self.valueBox.set(self.tableId, key: self.key(id), value: sharedWriteBuffer.readBufferNoCopy())
}
return .Reference
} else {
return .Embed(media)
}
} else {
sharedWriteBuffer.reset()
var type: Int8 = MediaEntryType.MessageReference.rawValue
sharedWriteBuffer.write(&type, offset: 0, length: 1)
var idPeerId: Int64 = index.id.peerId.toInt64()
var idNamespace: Int32 = index.id.namespace
var idId: Int32 = index.id.id
var idTimestamp: Int32 = index.timestamp
sharedWriteBuffer.write(&idPeerId, offset: 0, length: 8)
sharedWriteBuffer.write(&idNamespace, offset: 0, length: 4)
sharedWriteBuffer.write(&idId, offset: 0, length: 4)
sharedWriteBuffer.write(&idTimestamp, offset: 0, length: 4)
self.valueBox.set(self.tableId, key: self.key(id), value: sharedWriteBuffer.readBufferNoCopy())
return .Embed(media)
}
} else {
return .Embed(media)
}
}
func removeReference(id: MediaId, sharedWriteBuffer: WriteBuffer = WriteBuffer()) {
if let value = self.valueBox.get(self.tableId, key: self.key(id)) {
var type: Int8 = 0
value.read(&type, offset: 0, length: 1)
if type == MediaEntryType.Direct.rawValue {
var dataLength: Int32 = 0
value.read(&dataLength, offset: 0, length: 4)
let mediaOffset = value.offset
value.skip(Int(dataLength))
sharedWriteBuffer.reset()
sharedWriteBuffer.write(value.memory, offset: 0, length: value.offset)
var messageReferenceCount: Int32 = 0
value.read(&messageReferenceCount, offset: 0, length: 4)
messageReferenceCount -= 1
sharedWriteBuffer.write(&messageReferenceCount, offset: 0, length: 4)
if messageReferenceCount <= 0 {
if let media = Decoder(buffer: MemoryBuffer(memory: value.memory + mediaOffset, capacity: Int(dataLength), length: Int(dataLength), freeWhenDone: false)).decodeRootObject() as? Media {
self.mediaCleanupTable.add(media)
}
self.valueBox.remove(self.tableId, key: self.key(id))
} else {
self.valueBox.set(self.tableId, key: self.key(id), value: sharedWriteBuffer.readBufferNoCopy())
}
} else if type == MediaEntryType.MessageReference.rawValue {
self.valueBox.remove(self.tableId, key: self.key(id))
}
}
}
func removeEmbeddedMedia(media: Media) {
if let id = media.id {
self.valueBox.remove(self.tableId, key: self.key(id))
}
self.mediaCleanupTable.add(media)
}
func debugList() -> [DebugMediaEntry] {
var entries: [DebugMediaEntry] = []
let upperBoundKey = ValueBoxKey(length: 8 + 4)
memset(upperBoundKey.memory, 0xff, 8 + 4)
self.valueBox.range(self.tableId, start: ValueBoxKey(length: 0), end: upperBoundKey, values: { key, value in
var type: Int8 = 0
value.read(&type, offset: 0, length: 1)
if type == MediaEntryType.Direct.rawValue {
var dataLength: Int32 = 0
value.read(&dataLength, offset: 0, length: 4)
if let media = Decoder(buffer: MemoryBuffer(memory: value.memory + value.offset, capacity: Int(dataLength), length: Int(dataLength), freeWhenDone: false)).decodeRootObject() as? Media {
value.skip(Int(dataLength))
var messageReferenceCount: Int32 = 0
value.read(&messageReferenceCount, offset: 0, length: 4)
entries.append(.Direct(media, Int(messageReferenceCount)))
}
} else if type == MediaEntryType.MessageReference.rawValue {
var idPeerId: Int64 = 0
var idNamespace: Int32 = 0
var idId: Int32 = 0
var idTimestamp: Int32 = 0
value.read(&idPeerId, offset: 0, length: 8)
value.read(&idNamespace, offset: 0, length: 4)
value.read(&idId, offset: 0, length: 4)
value.read(&idTimestamp, offset: 0, length: 4)
let referencedMessageIndex = MessageIndex(id: MessageId(peerId: PeerId(idPeerId), namespace: idNamespace, id: idId), timestamp: idTimestamp)
entries.append(.MessageReference(referencedMessageIndex))
}
return true
}, limit: 1000)
return entries
}
}