Jump To …

ListNode.coffee

EventEmitter = (require 'events').EventEmitter
Addr64 = (require '../log/Addr64.coffee').Addr64
DataIterator = (require '../log/DataIterator.coffee').DataIterator
LoggableToWrite = (require '../log/LoggableToWrite.coffee').LoggableToWrite
LightOutputStream =
  (require '../database/impl/iterate/LightOutputStream.coffee').
  LightOutputStream
RandomAccessLoggable =
  (require '../log/RandomAccessLoggable.coffee').RandomAccessLoggable
CompressedUnsignedLongByteIterable =
  (require '../log/iterate/CompressedUnsignedLongByteIterable.coffee').
  CompressedUnsignedLongByteIterable
Log = (require '../log/Log.coffee').Log
LogUtil = (require '../log/LogUtil.coffee').LogUtil
ArrayByteIterable =
  (require '../database/impl/iterate/ArrayByteIterable.coffee').
  ArrayByteIterable
FixedLengthByteIterable =
  (require '../log/iterate/FixedLengthByteIterable.coffee').
  FixedLengthByteIterable

This class provides logical node of List. In fact value and virtual addresses are be stored separately.

class ListNode extends EventEmitter
  @NODE_TYPE: 3
  @KEY_TYPE: 5
  @VALUE_TYPE: 7

@private

  maxInt32: undefined

@private

  nullAddr: undefined

@private

  log: undefined

@private

  keyAddr: undefined

@private

  valueAddr: undefined

@private

  nextAddr: undefined

@private

  ownAddr: undefined

@private

  key: undefined

@private

  keyLength: undefined

@private

  value: undefined

@private

  valueLength: undefined

@private

  next: undefined

@private

  structureId: undefined

@private

  prev: undefined

@private

  addrsAreInMemory: undefined

Constructor.

@param log.

  @create$Log: (log, o) ->
    if !o? then o = new ListNode
    o.maxInt32 = (Math.pow 2, 32) - 1
    o.nullAddr = Addr64.create$int$int o.maxInt32, o.maxInt32
    o.log = log
    o.keyAddr = o.nullAddr
    o.valueAddr = o.nullAddr
    o.nextAddr = o.nullAddr
    o.ownAddr = o.nullAddr
    o.setMaxListeners 0
    o.addrsAreInMemory = true
    return o

Constructor.

@param log. @param key. @param value. @param next can be undefined.

  @create$Log$ByteIterable$ByteIterable$int$ListNode: (log, key, value, structureId, next, o) ->
    if !o? then o = new ListNode
    o = ListNode.create$Log log, o
    o.key = key
    o.keyLength = key.getLength()
    o.value = value
    o.valueLength = value.getLength()
    o.structureId = structureId
    o.next = next
    o.prev = undefined
    o.addrsAreInMemory = false
    return o

Check whether node is uploaded.

@return true if node is defined and it's key and value are defined or do not exist.

  nodeIsUploaded: () ->
    return !@addrsAreInMemory &&
    (@next? || @nextAddr.equals$Addr64 @nullAddr) &&
    (@key? || @keyAddr.equals$Addr64 @nullAddr) &&
    (@value? || @valueAddr.equals$Addr64 @nullAddr)

Wrap the node into LogGable object.

@return loggable.

  toLoggable: () ->
    length =
      (CompressedUnsignedLongByteIterable.getCompressedSize$int @nextAddr.left) +
      (CompressedUnsignedLongByteIterable.getCompressedSize$int @nextAddr.right) +
      (CompressedUnsignedLongByteIterable.getCompressedSize$int @keyAddr.left) +
      (CompressedUnsignedLongByteIterable.getCompressedSize$int @keyAddr.right) +
      (CompressedUnsignedLongByteIterable.getCompressedSize$int @valueAddr.left) +
      (CompressedUnsignedLongByteIterable.getCompressedSize$int @valueAddr.right)
    stream = LightOutputStream.create$int length
    CompressedUnsignedLongByteIterable.
    fillBytes$int$LightOutputStream @nextAddr.left, stream
    CompressedUnsignedLongByteIterable.
    fillBytes$int$LightOutputStream @nextAddr.right, stream
    CompressedUnsignedLongByteIterable.
    fillBytes$int$LightOutputStream @keyAddr.left, stream
    CompressedUnsignedLongByteIterable.
    fillBytes$int$LightOutputStream @keyAddr.right, stream
    CompressedUnsignedLongByteIterable.
    fillBytes$int$LightOutputStream @valueAddr.left, stream
    CompressedUnsignedLongByteIterable.
    fillBytes$int$LightOutputStream @valueAddr.right, stream
    loggable = LoggableToWrite.create$int$ByteIterable$int ListNode.NODE_TYPE,
            stream.asArrayByteIterable(), @structureId
    return loggable

Wrap the key that is stored in the node into Logable object.

@return loggable.

  keyToLoggable: () ->
    return LoggableToWrite.create$int$ByteIterable$int ListNode.KEY_TYPE, @key,
            @structureId

Wrap the value that is stored in the node into Logable object.

@return loggable.

  valueToLoggable: () ->
    return LoggableToWrite.create$int$ByteIterable$int ListNode.VALUE_TYPE,
            @value, @structureId

Save to Log this node and all the nodes before him (in reversed order) if it is necessary. This function doesn't commit data to Log.

@return address of this node in log.

  flush$emit: () ->
    if @next? && @nextAddr.equals$Addr64 @nullAddr
      @nextAddr = @next.flush$emit()
    if @key? && @keyAddr.equals$Addr64 @nullAddr
      @keyAddr = @log.write$Loggable @keyToLoggable()
    if @value? && @valueAddr.equals$Addr64 @nullAddr
      @valueAddr = @log.write$Loggable @valueToLoggable()
    if @ownAddr.equals$Addr64 @nullAddr
      @ownAddr = @log.write$Loggable @toLoggable()
      return @ownAddr

  isEmpty: () ->
    return (!@next? && @nextAddr.equals$Addr64 @nullAddr) &&
    (!@key? && @keyAddr.equals$Addr64 @nullAddr) &&
    (!@value? && @valueAddr.equals$Addr64 @nullAddr)

Get node itself - all adresses but not key and value.

@return always emits 'getSelf' when done.

  getSelf: () ->
    if @nodeIsUploaded()
      @emit 'getSelf'
    else
      if @ownAddr.equals$Addr64(@nullAddr) ||
      @ownAddr.equals$Addr64 Addr64.create$int$int(-1, -1)
        @emit 'getSelf'
      else
        addrStr = Addr64.addr64ToString$Addr64$int @ownAddr, LogUtil.LOG_NAME_BASE
        @log.on 'read' + addrStr, (loggable) =>
          it = loggable.getData().iterator()
          nums = new Object()
          n = 0
          onRead = (value) =>
            n += 1
            nums[n] = value
            if n == 6
              @nextAddr = Addr64.create$int$int nums[1], nums[2]
              @keyAddr = Addr64.create$int$int nums[3], nums[4]
              @valueAddr = Addr64.create$int$int nums[5], nums[6]
              @addrsAreInMemory = false
              @emit 'getSelf'
            else
              value = CompressedUnsignedLongByteIterable.
              getInt$ByteIterator$emit it
              if value?
                onRead value
          CompressedUnsignedLongByteIterable.EMITTER.on 'getInt', (value) =>
            onRead value
          value = CompressedUnsignedLongByteIterable.getInt$ByteIterator$emit it
          if value?
            onRead value
        @log.read$Addr64$emit @ownAddr

Get node key.

@return always emits 'getKey' when done.

  getKey: () ->
    if @key?
      @emit 'getKey', @key
    else if @keyAddr.equals$Addr64 @nullAddr
      @emit 'getKey', undefined
    else
      addrStr = Addr64.addr64ToString$Addr64$int @keyAddr, LogUtil.LOG_NAME_BASE
      @log.on 'read' + addrStr, (loggable) =>
        @key = FixedLengthByteIterable.
        create$ByteIterable$int loggable.getData(), loggable.getDataLength()
        @emit 'getKey', @key
      @log.read$Addr64$emit @keyAddr

Get node value.

@return always emits 'getValue' when done.

  getValue: () ->
    if @value?
      @emit 'getValue', @value
    else if @valueAddr.equals$Addr64 @nullAddr
      @emit 'getValue', undefined
    else
      addrStr = Addr64.addr64ToString$Addr64$int @valueAddr, LogUtil.LOG_NAME_BASE
      @log.on 'read' + addrStr, (loggable) =>
        @value = FixedLengthByteIterable.
        create$ByteIterable$int loggable.getData(), loggable.getDataLength()
        @emit 'getValue', @value
      @log.read$Addr64$emit @valueAddr

Get next node.

@return next.

  getNext: () ->
    if !@next?
      if !@nextAddr.equals$Addr64 @nullAddr
        node = ListNode.create$Log @log
        node.ownAddr = @nextAddr
        return node
      else
        return undefined
    else
      return @next

Get all node data - all addresses, key and value.

@return always emits 'getNode' when done. If true, node is got, otherwise node is empty.

  getNode: () ->
    this.once 'getSelf', () =>
      if @isEmpty()
        @emit 'getNode', false, this
      else
        @next = @getNext()
        getKey = false
        getValue = false
        this.once 'getKey', (key) =>
          getKey = true
          if getKey && getValue
            @emit 'getNode', true, this
        this.once 'getValue', (value) =>
          getValue = true
          if getKey && getValue
            @emit 'getNode', true, this
        if !@keyAddr.equals$Addr64 @nullAddr
          @getKey()
        else
          getKey = true
        if !@valueAddr.equals$Addr64 @nullAddr
          @getValue()
        else
          getValue = true
        if getKey && getValue
          @emit 'getNode', true, this
    if @ownAddr?
      @getSelf()
    else
      @emit 'getNode', false, this

exports.ListNode = ListNode