List.coffee | |
---|---|
Addr64 = (require '../log/Addr64.coffee').Addr64
ListNode = (require './ListNode.coffee').ListNode
EventEmitter = (require 'events').EventEmitter
RandomAccessByteIterable =
(require '../log/RandomAccessByteIterable.coffee').RandomAccessByteIterable
DatabaseRoot = (require '../list_env/DatabaseRoot.coffee').DatabaseRoot
ArrayByteIterable =
(require '../database/impl/iterate/ArrayByteIterable.coffee').
ArrayByteIterable
LightOutputStream =
(require '../database/impl/iterate/LightOutputStream.coffee').
LightOutputStream
CompressedUnsignedLongByteIterable =
(require '../log/iterate/CompressedUnsignedLongByteIterable.coffee').
CompressedUnsignedLongByteIterable | |
This class provides simple List, which can add elements, store itself in Log and restore from Log. It also can store changes (without overwriting all the Log). | class List extends EventEmitter |
@private | log: undefined |
@private | rootNode: undefined |
@private | dublicates: undefined |
@private | structureId: undefined |
@private | length: undefined
@EMITTER = new EventEmitter()
saveLengthToRoot: () ->
@rootNode.key = ArrayByteIterable.create$Buffer new Buffer("L")
l = CompressedUnsignedLongByteIterable.getCompressedSize$int @length
stream = LightOutputStream.create$int l
CompressedUnsignedLongByteIterable.
fillBytes$int$LightOutputStream @length, stream
@rootNode.value = stream.asArrayByteIterable()
@rootNode.addrsAreInMemory = false |
Constructor. @param log | @create$Log$Boolean$int: (log, dublicates, structureId, o) ->
if !o? then o = new List
o.log = log
o.dublicates = dublicates
o.structureId = structureId
o.rootNode = ListNode.create$Log log
o.log.setMaxListeners 0
o.setMaxListeners 0
o.emptyList = false
o.log.on 'getLastLoggableOfType', (loggable) =>
if loggable?
dr = DatabaseRoot.create$Addr64$int null, null
onReadFromLoggable = () =>
o.rootNode.ownAddr = dr.getRootAddress().copy()
if o.rootNode.ownAddr.equals$Addr64(Addr64.create$int$int 0, 0)
o.rootNode.ownAddr = o.rootNode.nullAddr
o.length = 0
o.saveLengthToRoot()
List.EMITTER.emit 'create', o
else
o.rootNode.once 'getNode', (ok, newnode) =>
o.rootNode = newnode
CompressedUnsignedLongByteIterable.EMITTER.once 'getInt', (value) =>
o.length = value
List.EMITTER.emit 'create', o
value = CompressedUnsignedLongByteIterable.
getInt$ByteIterator$emit o.rootNode.value.source.iterator()
if value?
o.length = value
List.EMITTER.emit 'create', o
o.rootNode.getNode()
dr.once 'readFromLoggable', () =>
onReadFromLoggable()
isValid = dr.readFromLoggable loggable
if isValid?
dr.removeAllListeners 'readFromLoggable'
onReadFromLoggable()
else
o.rootNode.ownAddr = o.rootNode.nullAddr
o.length = 0
o.saveLengthToRoot()
List.EMITTER.emit 'create', o
o.log.getLastLoggableOfType$int$emit(DatabaseRoot.DATABASE_ROOT_TYPE)
return undefined
getLength: () ->
return @length |
Save all the changes in List to Log and write Log on disk. @return always emit 'flush' event. | flush$emit: () ->
@rootNode.flush$emit()
@log.once 'flush', () =>
@emit 'flush'
@log.write$Loggable(DatabaseRoot.
create$Addr64$int(@rootNode.ownAddr, @structureId).toLoggable())
@log.flush$emit() |
Save all the changes in List to Log and close Log. @return always emit 'close' event. | close$emit: () ->
@rootNode.flush$emit()
@log.once 'close', () =>
@emit 'close'
@log.write$Loggable(DatabaseRoot.
create$Addr64$int(@rootNode.ownAddr, @structureId).toLoggable())
@log.close$emit() |
Check element for key equal. @param node. @param key. @param event the name of the event to be emitted. @return always emits the named event. | check$ListNode$ByteIterable$String$emit: (node, key, event) ->
onCompare = (res) =>
if res == 0
@emit event, node
else
oldNode = node
node = node.next
if node?
node.once 'getNode', (ok, newnode) =>
newnode.prev = oldNode
if ok
@check$ListNode$ByteIterable$String$emit newnode, key, event
else
@emit event, undefined
node.getNode()
else
@emit event, undefined
if node.isEmpty()
@emit event, undefined
else
node.key.once 'compare', (res) =>
onCompare res
res = node.key.compareTo$ByteIterable$emit key
if res?
if node.key?
node.key.removeAllListeners 'compare'
onCompare res |
@private Find element by key. @param key. @param event the name of the event to be emitted. @return always emits the named event. | find$ByteIterable$String$emit: (key, event) ->
node = @rootNode
node.once 'getNode', (ok, node) =>
if ok
if node.key?
@check$ListNode$ByteIterable$String$emit node, key, event
else
@emit event, undefined
else
@emit event, undefined
node.getNode() |
Find element by key. @param key. @return always emits the 'get' event. | get$ByteIterable$emit: (key) ->
@find$ByteIterable$String$emit key, 'get' |
@private Set new value to the targetNode. Use it instead of simply ListNode::value = value! This function provides necessary changes in List elements, do that it will be correctly saved to Log. @param targetNode. @param value. | setValue$ListNode$ByteIterable$emit: (targetNode, value) ->
targetNode.value = value
targetNode.valueAddr = targetNode.nullAddr
targetNode.ownAddr = targetNode.nullAddr
node = @rootNode
while node != targetNode
node.ownAddr = node.nullAddr
node.nextAddr = node.nullAddr
node = node.next |
@private Add node. @param key @param value | addNode$ByteIterable$ByteIterable: (key, value) ->
newNode = ListNode.create$Log$ByteIterable$ByteIterable$int$ListNode @log,
key, value, @structureId, @rootNode.next
newNode.nextAddr = @rootNode.nextAddr
newNode.prev = @rootNode
@rootNode.next = newNode
@rootNode.nextAddr = @rootNode.nullAddr
@length += 1
@saveLengthToRoot() |
Add new element with specified value to List. @param key the key of the new element. If element already exists, it's value is overwritten. @param value. @return always emits 'push' event. | push$ByteIterable$ByteIterable$emit: (key, value) ->
if @dublicates
@addNode$ByteIterable$ByteIterable key, value
@emit 'push'
else
@once 'getValue', (node) =>
if !node?
@addNode$ByteIterable$ByteIterable key, value
@emit 'push'
else
@setValue$ListNode$ByteIterable$emit node, value
@emit 'push'
@find$ByteIterable$String$emit key, 'getValue' |
Add new element with specified value to List. @param key the key of the new element. @param value. @return always emits 'add' event with true/false (see Store::add). | add$ByteIterable$ByteIterable$emit: (key, value) ->
@once 'getValue', (node) =>
if !node?
@addNode$ByteIterable$ByteIterable key, value
@emit 'add', true
else
@emit 'add', false
@find$ByteIterable$String$emit key, 'getValue' |
Delete this exact node. @param node @return previous node | deleteNode$ListNode: (node) ->
node.prev.nextAddr = node.nextAddr
node.prev.next = node.next
if node.next?
node.next.prev = node.prev
@setValue$ListNode$ByteIterable$emit node.prev, node.prev.value
node = node.prev
@length -= 1
@saveLengthToRoot()
return node |
Delete all the nodes with given key. @param key. @return always emits 'delete' event. | delete$ByteIterable$emit: (key) ->
@once 'getValueForDelete', (node) =>
if !node?
@emit 'delete', key
else
@deleteNode$ListNode node
@delete$ByteIterable$emit key
@find$ByteIterable$String$emit key, 'getValueForDelete' |
Print list to console (debug only!). | print: () ->
node = @rootNode
console.log "List in memory:"
while node? && node.key?
console.log "key =", node.key.getBytesUnsafe(),
"prev_key =", (if node.prev? then node.prev.key.getBytesUnsafe()),
"value =", node.value.getBytesUnsafe(),
"nextAddr =", node.nextAddr.left, ":", node.nextAddr.right
node = node.next
console.log "End of dump"
exports.List = List
|