 
   Blocks have two primary attributes, `:id` and `:size`. The block identifier
   is a multihash with the digest identifying the content. The size is the
-  number of bytes in the block content.
+  number of bytes in the block content. Blocks also have a `:stored-at` value
+  giving the instant they were persisted, but this does not affect equality or
+  the block's hash code.
 
   Internally, blocks may reference their content in-memory as a byte array, or
   a _content reader_ which constructs new input streams for the block data on

(alter-meta! #'->Block assoc :private true)


(defn lazy-block
  "Creates a block from a content reader. The simplest version is a no-arg
  function which should return a new `InputStream` to read the full block
  content. The block is given the id and size directly, without being checked."
  ^blocks.data.Block
  [id size reader]
  (->Block id size reader nil nil))


(defn load-block
  "Creates a block by reading a source into memory. The block is given the id
  directly, without being checked."
  ^blocks.data.Block
  [id source]
  (let [content (collect-bytes source)]
    (when (pos? (count content))
      (->Block id (count content) content nil nil))))


(defn read-block
  "Creates a block by reading the source into memory and hashing it."
  ^blocks.data.Block
  [algorithm source]
  ; OPTIMIZE: calculate the hash while reading the content in one pass.
  (let [hash-fn (checked-hasher algorithm)
        content (collect-bytes source)]
    (when (pos? (count content))
      (->Block (hash-fn (read-all content))
               (count content)
               content
               nil
               nil))))


(defn wrap-block
  "Wrap a block's content by calling `f` on it, returning a new block with the
  same id and size."
  ^blocks.data.Block
  [^Block block f]
  (->Block (.id block)
           (.size block)
           (f (.content block))
           (._attrs block)
           (._meta block)))


(defn clean-block
  "Creates a version of the given block without extra attributes or metadata."
  [^Block block]
  (->Block (.id block)
           (.size block)
           (.content block)
           nil
           nil))


(defn merge-blocks
  "Creates a new block by merging together two blocks representing the same
  content. Block ids and sizes must match. The new block's content comes from
  the second block, and any extra attributes and metadata are merged together."
  [^Block a ^Block b]
  (when (not= (.id a) (.id b))
    (throw (IllegalArgumentException.
             (str "Cannot merge blocks with differing ids " (.id a)
                  " and " (.id b)))))
  (->Block (.id b)
           (.size b)
           (.content b)
           (not-empty (merge (._attrs a) (._attrs b)))
           (not-empty (merge (._meta  a) (._meta  b)))))

    (PersistentBytes/wrap (bytes/to-byte-array source))))



;; ## Constructors

