Commit ce7ad2e2 authored by Juan Batiz-Benet's avatar Juan Batiz-Benet

Merge pull request #509 from jbenet/ipfs-object-stat

ipfs object learned stat
parents f862933c 6b279533
......@@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"strings"
......@@ -36,6 +37,7 @@ ipfs object get <key> - Get the DAG node named by <key>
ipfs object put <data> <encoding> - Stores input, outputs its key
ipfs object data <key> - Outputs raw bytes in an object
ipfs object links <key> - Outputs links pointed to by object
ipfs object stat <key> - Outputs statistics of object
`,
},
......@@ -44,6 +46,7 @@ ipfs object links <key> - Outputs links pointed to by object
"links": objectLinksCmd,
"get": objectGetCmd,
"put": objectPutCmd,
"stat": objectStatCmd,
},
}
......@@ -180,6 +183,64 @@ This command outputs data in the following encodings:
},
}
var objectStatCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Get stats for the DAG node named by <key>",
ShortDescription: `
'ipfs object stat' is a plumbing command to print DAG node statistics.
<key> is a base58 encoded multihash. It outputs to stdout:
NumLinks int number of links in link table
BlockSize int size of the raw, encoded data
LinksSize int size of the links segment
DataSize int size of the data segment
CumulativeSize int cumulative size of object and its references
`,
},
Arguments: []cmds.Argument{
cmds.StringArg("key", true, false, "Key of the object to retrieve (in base58-encoded multihash format)"),
},
Run: func(req cmds.Request) (interface{}, error) {
n, err := req.Context().GetNode()
if err != nil {
return nil, err
}
key := req.Arguments()[0]
object, err := objectGet(n, key)
if err != nil {
return nil, err
}
ns, err := object.Stat()
if err != nil {
return nil, err
}
return ns, nil
},
Type: dag.NodeStat{},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) (io.Reader, error) {
ns := res.Output().(dag.NodeStat)
var buf bytes.Buffer
w := func(s string, n int) {
buf.Write([]byte(fmt.Sprintf("%s: %d\n", s, n)))
}
w("NumLinks", ns.NumLinks)
w("BlockSize", ns.BlockSize)
w("LinksSize", ns.LinksSize)
w("DataSize", ns.DataSize)
w("CumulativeSize", ns.CumulativeSize)
return &buf, nil
},
},
}
var objectPutCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Stores input as a DAG object, outputs its key",
......
......@@ -9,7 +9,6 @@ import (
"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
blocks "github.com/jbenet/go-ipfs/blocks"
bserv "github.com/jbenet/go-ipfs/blockservice"
u "github.com/jbenet/go-ipfs/util"
......@@ -18,11 +17,6 @@ import (
var log = u.Logger("merkledag")
var ErrNotFound = fmt.Errorf("merkledag: not found")
// NodeMap maps u.Keys to Nodes.
// We cannot use []byte/Multihash for keys :(
// so have to convert Multihash bytes to string (u.Key)
type NodeMap map[u.Key]*Node
// DAGService is an IPFS Merkle DAG service.
type DAGService interface {
Add(*Node) (u.Key, error)
......@@ -39,146 +33,6 @@ func NewDAGService(bs *bserv.BlockService) DAGService {
return &dagService{bs}
}
// Node represents a node in the IPFS Merkle DAG.
// nodes have opaque data and a set of navigable links.
type Node struct {
Links []*Link
Data []byte
// cache encoded/marshaled value
encoded []byte
cached mh.Multihash
}
// Link represents an IPFS Merkle DAG Link between Nodes.
type Link struct {
// utf string name. should be unique per object
Name string // utf8
// cumulative size of target object
Size uint64
// multihash of the target object
Hash mh.Multihash
// a ptr to the actual node for graph manipulation
Node *Node
}
type LinkSlice []*Link
func (ls LinkSlice) Len() int { return len(ls) }
func (ls LinkSlice) Swap(a, b int) { ls[a], ls[b] = ls[b], ls[a] }
func (ls LinkSlice) Less(a, b int) bool { return ls[a].Name < ls[b].Name }
// MakeLink creates a link to the given node
func MakeLink(n *Node) (*Link, error) {
s, err := n.Size()
if err != nil {
return nil, err
}
h, err := n.Multihash()
if err != nil {
return nil, err
}
return &Link{
Size: s,
Hash: h,
}, nil
}
// GetNode returns the MDAG Node that this link points to
func (l *Link) GetNode(serv DAGService) (*Node, error) {
if l.Node != nil {
return l.Node, nil
}
return serv.Get(u.Key(l.Hash))
}
// AddNodeLink adds a link to another node.
func (n *Node) AddNodeLink(name string, that *Node) error {
lnk, err := MakeLink(that)
if err != nil {
return err
}
lnk.Name = name
lnk.Node = that
n.Links = append(n.Links, lnk)
return nil
}
// AddNodeLink adds a link to another node. without keeping a reference to
// the child node
func (n *Node) AddNodeLinkClean(name string, that *Node) error {
lnk, err := MakeLink(that)
if err != nil {
return err
}
lnk.Name = name
n.Links = append(n.Links, lnk)
return nil
}
// Remove a link on this node by the given name
func (n *Node) RemoveNodeLink(name string) error {
for i, l := range n.Links {
if l.Name == name {
n.Links = append(n.Links[:i], n.Links[i+1:]...)
return nil
}
}
return ErrNotFound
}
// Copy returns a copy of the node.
// NOTE: does not make copies of Node objects in the links.
func (n *Node) Copy() *Node {
nnode := new(Node)
nnode.Data = make([]byte, len(n.Data))
copy(nnode.Data, n.Data)
nnode.Links = make([]*Link, len(n.Links))
copy(nnode.Links, n.Links)
return nnode
}
// Size returns the total size of the data addressed by node,
// including the total sizes of references.
func (n *Node) Size() (uint64, error) {
b, err := n.Encoded(false)
if err != nil {
return 0, err
}
s := uint64(len(b))
for _, l := range n.Links {
s += l.Size
}
return s, nil
}
// Multihash hashes the encoded data of this node.
func (n *Node) Multihash() (mh.Multihash, error) {
// Note: Encoded generates the hash and puts it in n.cached.
_, err := n.Encoded(false)
if err != nil {
return nil, err
}
return n.cached, nil
}
// Key returns the Multihash as a key, for maps.
func (n *Node) Key() (u.Key, error) {
h, err := n.Multihash()
return u.Key(h), err
}
// dagService is an IPFS Merkle DAG service.
// - the root is virtual (like a forest)
// - stores nodes' data in a BlockService
......
......@@ -85,6 +85,8 @@ func TestNode(t *testing.T) {
} else {
fmt.Println("key: ", k)
}
SubtestNodeStat(t, n)
}
printn("beep", n1)
......@@ -92,6 +94,40 @@ func TestNode(t *testing.T) {
printn("beep boop", n3)
}
func SubtestNodeStat(t *testing.T, n *Node) {
enc, err := n.Encoded(true)
if err != nil {
t.Error("n.Encoded(true) failed")
return
}
cumSize, err := n.Size()
if err != nil {
t.Error("n.Size() failed")
return
}
expected := NodeStat{
NumLinks: len(n.Links),
BlockSize: len(enc),
LinksSize: len(enc) - len(n.Data), // includes framing.
DataSize: len(n.Data),
CumulativeSize: int(cumSize),
}
actual, err := n.Stat()
if err != nil {
t.Error("n.Stat() failed")
return
}
if expected != actual {
t.Error("n.Stat incorrect.\nexpect: %s\nactual: %s", expected, actual)
} else {
fmt.Printf("n.Stat correct: %s\n", actual)
}
}
type devZero struct{}
func (_ devZero) Read(b []byte) (int, error) {
......
package merkledag
import (
"fmt"
mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
u "github.com/jbenet/go-ipfs/util"
)
// NodeMap maps u.Keys to Nodes.
// We cannot use []byte/Multihash for keys :(
// so have to convert Multihash bytes to string (u.Key)
type NodeMap map[u.Key]*Node
// Node represents a node in the IPFS Merkle DAG.
// nodes have opaque data and a set of navigable links.
type Node struct {
Links []*Link
Data []byte
// cache encoded/marshaled value
encoded []byte
cached mh.Multihash
}
// NodeStat is a statistics object for a Node. Mostly sizes.
type NodeStat struct {
NumLinks int // number of links in link table
BlockSize int // size of the raw, encoded data
LinksSize int // size of the links segment
DataSize int // size of the data segment
CumulativeSize int // cumulative size of object and its references
}
func (ns NodeStat) String() string {
f := "NodeStat{NumLinks: %d, BlockSize: %d, LinksSize: %d, DataSize: %d, CumulativeSize: %d}"
return fmt.Sprintf(f, ns.NumLinks, ns.BlockSize, ns.LinksSize, ns.DataSize, ns.CumulativeSize)
}
// Link represents an IPFS Merkle DAG Link between Nodes.
type Link struct {
// utf string name. should be unique per object
Name string // utf8
// cumulative size of target object
Size uint64
// multihash of the target object
Hash mh.Multihash
// a ptr to the actual node for graph manipulation
Node *Node
}
type LinkSlice []*Link
func (ls LinkSlice) Len() int { return len(ls) }
func (ls LinkSlice) Swap(a, b int) { ls[a], ls[b] = ls[b], ls[a] }
func (ls LinkSlice) Less(a, b int) bool { return ls[a].Name < ls[b].Name }
// MakeLink creates a link to the given node
func MakeLink(n *Node) (*Link, error) {
s, err := n.Size()
if err != nil {
return nil, err
}
h, err := n.Multihash()
if err != nil {
return nil, err
}
return &Link{
Size: s,
Hash: h,
}, nil
}
// GetNode returns the MDAG Node that this link points to
func (l *Link) GetNode(serv DAGService) (*Node, error) {
if l.Node != nil {
return l.Node, nil
}
return serv.Get(u.Key(l.Hash))
}
// AddNodeLink adds a link to another node.
func (n *Node) AddNodeLink(name string, that *Node) error {
lnk, err := MakeLink(that)
if err != nil {
return err
}
lnk.Name = name
lnk.Node = that
n.Links = append(n.Links, lnk)
return nil
}
// AddNodeLink adds a link to another node. without keeping a reference to
// the child node
func (n *Node) AddNodeLinkClean(name string, that *Node) error {
lnk, err := MakeLink(that)
if err != nil {
return err
}
lnk.Name = name
n.Links = append(n.Links, lnk)
return nil
}
// Remove a link on this node by the given name
func (n *Node) RemoveNodeLink(name string) error {
for i, l := range n.Links {
if l.Name == name {
n.Links = append(n.Links[:i], n.Links[i+1:]...)
return nil
}
}
return ErrNotFound
}
// Copy returns a copy of the node.
// NOTE: does not make copies of Node objects in the links.
func (n *Node) Copy() *Node {
nnode := new(Node)
nnode.Data = make([]byte, len(n.Data))
copy(nnode.Data, n.Data)
nnode.Links = make([]*Link, len(n.Links))
copy(nnode.Links, n.Links)
return nnode
}
// Size returns the total size of the data addressed by node,
// including the total sizes of references.
func (n *Node) Size() (uint64, error) {
b, err := n.Encoded(false)
if err != nil {
return 0, err
}
s := uint64(len(b))
for _, l := range n.Links {
s += l.Size
}
return s, nil
}
// Stat returns statistics on the node.
func (n *Node) Stat() (NodeStat, error) {
enc, err := n.Encoded(false)
if err != nil {
return NodeStat{}, err
}
cumSize, err := n.Size()
if err != nil {
return NodeStat{}, err
}
return NodeStat{
NumLinks: len(n.Links),
BlockSize: len(enc),
LinksSize: len(enc) - len(n.Data), // includes framing.
DataSize: len(n.Data),
CumulativeSize: int(cumSize),
}, nil
}
// Multihash hashes the encoded data of this node.
func (n *Node) Multihash() (mh.Multihash, error) {
// Note: Encoded generates the hash and puts it in n.cached.
_, err := n.Encoded(false)
if err != nil {
return nil, err
}
return n.cached, nil
}
// Key returns the Multihash as a key, for maps.
func (n *Node) Key() (u.Key, error) {
h, err := n.Multihash()
return u.Key(h), err
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment