Jump To …

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