Sharded IPLD Array

The Sharray is an IPLD tree structure used to store an array of items. It is designed for usecases that know all items at the time of creation and do not need insertion or deletion.

IPLD Representation

Each sharray node is represented by an IPLD node of the following schema:

type Node struct {
  height Int
  items [Item]
} representation tuple

Item may be either a direct value, if height == 0, or the Cid of a child node if height > 0.

(For details on IPLD Schemas, see the IPLD Schema Spec (draft))

We use DAG-CBOR for serialization, and blake2b-256 for hashing.

Construction

The tree must not be sparse. Given an array of size N and a fixed width of W:

  • The left floor(N/W) leaves contain the first N items.

  • If N % W != 0 the final leaf contains the final remainder.

  • The tree is perfectly balanced.

  • The height is the distance from the leaves, not the root.

  • Leaves (nodes with a height of 0) contain array values.

  • Inner nodes (nodes with height greater than zero) contain the cids of their child nodes.

Operations

create(items)

Create a sharray from a given set of items

func create(items []Item) Cid {
    var layer cidQueue

    itemQ := queue(items)
    for !itemQ.Empty() {
        // get the next 'Width' items from the input items
        vals := itemQ.PopN(width)

        nd := Node{
            height: 0,
            items:  vals,
        }

        // persist the node to the datastore
        storeNode(nd)

        layer.push(nd.Cid())
    }

    var nextLayer cidQueue
    for height := 1; layer.Len() > 1; height++ {
        for layer.Len() > 0 {
            vals := layer.PopN(width)

            nd := Node{
                height: height,
                items:  vals,
            }

            storeNode(nd)

            nextLayer.append(nd.Cid())
        }
        layer = nextLayer
        nextLayer.ClearItems()
    }

    return nextLayer.First()
}

get(i)

Get the element at index i

func (n node) get(i int) Item {
    if n.Height == 0 {
        return n.Array[i]
    }

    childWidth := Pow(Width, n.Height)

    child := loadNode(n.Array[i/childWidth])
    return child.get(i % childWidth)
}

Last updated