repo_description: Reference implementation of the Filecoin protocol, written in Go
repo_topics: blockchain filecoin golang ipfs
code: package main
  
  import (
  	"encoding/base64"
  	"encoding/hex"
  	"fmt"
  	"io"
  	"os"
  
  	"github.com/urfave/cli/v2"
  	"golang.org/x/xerrors"
  
  	"github.com/filecoin-project/go-bitfield"
  	rlepluslazy "github.com/filecoin-project/go-bitfield/rle"
  )
  
  var bitFieldCmd = &cli.Command{
  	Name:        "bitfield",
  	Usage:       "Bitfield analyze tool",
  	Description: "analyze bitfields",
  	Flags: []cli.Flag{
  		&cli.StringFlag{
  			Name:  "enc",
  			Value: "base64",
  			Usage: "specify input encoding to parse",
  		},
  	},
  	Subcommands: []*cli.Command{
  		bitFieldEncodeCmd,
  		bitFieldDecodeCmd,
  		bitFieldRunsCmd,
  		bitFieldStatCmd,
  		bitFieldMergeCmd,
  		bitFieldIntersectCmd,
  		bitFieldSubCmd,
  	},
  }
  
  var bitFieldRunsCmd = &cli.Command{
  	Name:        "runs",
  	Usage:       "Bitfield bit runs",
  	Description: "print bit runs in a bitfield",
  	Action: func(cctx *cli.Context) error {
  		dec, err := decodeToByte(cctx, 0)
  		if err != nil {
  			return err
  		}
  
  		rle, err := rlepluslazy.FromBuf(dec)
  		if err != nil {
  			return xerrors.Errorf("opening rle: %w", err)
  		}
  
  		rit, err := rle.RunIterator()
  		if err != nil {
  			return xerrors.Errorf("getting run iterator: %w", err)
  		}
  		var idx uint64
  		for rit.HasNext() {
  			r, err := rit.NextRun()
  			if err != nil {
  				return xerrors.Errorf("next run: %w", err)
  			}
  			if !r.Valid() {
  				fmt.Print("!INVALID ")
  			}
  			s := "TRUE "
  			if !r.Val {
  				s = "FALSE"
  			}
  
  			fmt.Printf("@%08d %s * %d\n", idx, s, r.Len)
  
  			idx += r.Len
  		}
  
  		return nil
  	},
  }
  
  var bitFieldStatCmd = &cli.Command{
  	Name:        "stat",
  	Usage:       "Bitfield stats",
  	Description: "print bitfield stats",
  	Action: func(cctx *cli.Context) error {
  		dec, err := decodeToByte(cctx, 0)
  		if err != nil {
  			return err
  		}
  		fmt.Printf("Raw length: %d bits (%d bytes)\n", len(dec)*8, len(dec))
  
  		rle, err := rlepluslazy.FromBuf(dec)
  		if err != nil {
  			return xerrors.Errorf("opening rle: %w", err)
  		}
  
  		rit, err := rle.RunIterator()
  		if err != nil {
  			return xerrors.Errorf("getting run iterator: %w", err)
  		}
  
  		var ones, zeros, oneRuns, zeroRuns, invalid uint64
  		for rit.HasNext() {
  			r, err := rit.NextRun()
  			if err != nil {
  				return xerrors.Errorf("next run: %w", err)
  			}
  			if !r.Valid() {
  				invalid++
  			}
  			if r.Val {
  				ones += r.Len
  				oneRuns++
  			} else {
  				zeros += r.Len
  				zeroRuns++
  			}
  		}
  
  		if _, err := rle.Count(); err != nil { // check overflows
  			fmt.Println("Error: ", err)
  		}
  
  		fmt.Printf("Decoded length: %d bits\n", ones+zeros)
  		fmt.Printf("\tOnes:  %d\n", ones)
  		fmt.Printf("\tZeros: %d\n", zeros)
  		fmt.Printf("Runs: %d\n", oneRuns+zeroRuns)
  		fmt.Printf("\tOne Runs:  %d\n", oneRuns)
  		fmt.Printf("\tZero Runs: %d\n", zeroRuns)
  		fmt.Printf("Invalid runs: %d\n", invalid)
  		return nil
  	},
  }
  
  var bitFieldDecodeCmd = &cli.Command{
  	Name:        "decode",
  	Usage:       "Bitfield to decimal number",
  	Description: "decode bitfield and print all numbers in it",
  	Action: func(cctx *cli.Context) error {
  		rle, err := decode(cctx, 0)
  		if err != nil {
  			return err
  		}
  
  		vals, err := rle.All(100000000000)
  		if err != nil {
  			return xerrors.Errorf("getting all items: %w", err)
  		}
  		fmt.Println(vals)
  
  		return nil
  	},
  }
  
  var bitFieldMergeCmd = &cli.Command{
  	Name:        "merge",
  	Usage:       "Merge 2 bitfields",
  	Description: "Merge 2 bitfields and print the resulting bitfield",
  	Action: func(cctx *cli.Context) error {
  		a, err := decode(cctx, 0)
  		if err != nil {
  			return err
  		}
  
  		b, err := decode(cctx, 1)
  		if err != nil {
  			return err
  		}
  
  		o, err := bitfield.MergeBitFields(a, b)
  		if err != nil {
  			return xerrors.Errorf("merge: %w", err)
  		}
  
  		str, err := encode(cctx, o)
  		if err != nil {
  			return err
  		}
  		fmt.Println(str)
  
  		return nil
  	},
  }
  
  var bitFieldIntersectCmd = &cli.Command{
  	Name:        "intersect",
  	Usage:       "Intersect 2 bitfields",
  	Description: "intersect 2 bitfields and print the resulting bitfield",
  	Action: func(cctx *cli.Context) error {
  		a, err := decode(cctx, 0)
  		if err != nil {
  			return err
  		}
  
  		b, err := decode(cctx, 1)
  		if err != nil {
  			return err
  		}
  
  		o, err := bitfield.IntersectBitField(a, b)
  		if err != nil {
  			return xerrors.Errorf("intersect: %w", err)
  		}
  
  		str, err := encode(cctx, o)
  		if err != nil {
  			return err
  		}
  		fmt.Println(str)
  
  		return nil
  	},
  }
  
  var bitFieldSubCmd = &cli.Command{
  	Name:        "sub",
  	Usage:       "Subtract 2 bitfields",
  	Description: "subtract 2 bitfields and print the resulting bitfield",
  	Action: func(cctx *cli.Context) error {
  		a, err := decode(cctx, 0)
  		if err != nil {
  			return err
  		}
  
  		b, err := decode(cctx, 1)
  		if err != nil {
  			return err
  		}
  
  		o, err := bitfield.SubtractBitField(a, b)
  		if err != nil {
  			return xerrors.Errorf("subtract: %w", err)
  		}
  
  		str, err := encode(cctx, o)
  		if err != nil {
  			return err
  		}
  		fmt.Println(str)
  
  		return nil
  	},
  }
  
  var bitFieldEncodeCmd = &cli.Command{
  	Name:        "encode",
  	Usage:       "Decimal number to bitfield",
  	Description: "encode a series of decimal numbers into a bitfield",
  	ArgsUsage:   "[infile]",
  	Action: func(cctx *cli.Context) error {
  		f, err := os.Open(cctx.Args().First())
  		if err != nil {
  			return err
  		}
  		defer f.Close() // nolint
  
  		out := bitfield.New()
  		for {
  			var i uint64
  			_, err := fmt.Fscan(f, &i)
  			if err == io.EOF {
  				break
  			}
  			out.Set(i)
  		}
  
  		str, err := encode(cctx, out)
  		if err != nil {
  			return err
  		}
  		fmt.Println(str)
  
  		return nil
  	},
  }
  
  func encode(cctx *cli.Context, field bitfield.BitField) (string, error) {
  	s, err := field.RunIterator()
  	if err != nil {
  		return "", err
  	}
  
  	bytes, err := rlepluslazy.EncodeRuns(s, []byte{})
  	if err != nil {
  		return "", err
  	}
  
  	var str string
  	switch cctx.String("enc") {
  	case "base64":
  		str = base64.StdEncoding.EncodeToString(bytes)
  	case "hex":
  		str = hex.EncodeToString(bytes)
  	default:
  		return "", fmt.Errorf("unrecognized encoding: %s", cctx.String("enc"))
  	}
  
  	return str, nil
  
  }
  func decode(cctx *cli.Context, i int) (bitfield.BitField, error) {
  	b, err := decodeToByte(cctx, i)
  	if err != nil {
  		return bitfield.BitField{}, err
  	}
  	return bitfield.NewFromBytes(b)
  }
  
  func decodeToByte(cctx *cli.Context, i int) ([]byte, error) {
  	var val string
  	if cctx.Args().Present() {
  		if i >= cctx.NArg() {
  			return nil, xerrors.Errorf("need more than %d args", i)
  		}
  		val = cctx.Args().Get(i)
  	} else {
  		if i > 0 {
  			return nil, xerrors.Errorf("need more than %d args", i)
  		}
  		r, err := io.ReadAll(os.Stdin)
  		if err != nil {
  			return nil, err
  		}
  		val = string(r)
  	}
  
  	var dec []byte
  	switch cctx.String("enc") {
  	case "base64":
  		d, err := base64.StdEncoding.DecodeString(val)
  		if err != nil {
  			return nil, fmt.Errorf("decoding base64 value: %w", err)
  		}
  		dec = d
  	case "hex":
  		d, err := hex.DecodeString(val)
  		if err != nil {
  			return nil, fmt.Errorf("decoding hex value: %w", err)
  		}
  		dec = d
  	default:
  		return nil, fmt.Errorf("unrecognized encoding: %s", cctx.String("enc"))
  	}
  
  	return dec, nil
  }
  