// CLIInfo outputs information about the ring or builder such as node count, // partition count, etc. // // Provide either the ring or the builder, but not both; set the other to nil. // Normally the results from RingOrBuilder. func CLIInfo(r Ring, b *Builder, output io.Writer) error { if r != nil { // TODO: Indication of how risky the assignments are: Replicas not in // distinct tiers, nodes. s := r.Stats() report := [][]string{ []string{brimtext.ThousandsSep(int64(s.PartitionCount), ","), "Partitions"}, []string{brimtext.ThousandsSep(int64(s.PartitionBitCount), ","), "Partition Bits"}, []string{brimtext.ThousandsSep(int64(r.ReplicaCount()), ","), "Replicas"}, []string{brimtext.ThousandsSep(int64(s.ActiveNodeCount), ","), "Active Nodes"}, []string{brimtext.ThousandsSep(int64(s.InactiveNodeCount), ","), "Inactive Nodes"}, []string{brimtext.ThousandsSepU(s.ActiveCapacity, ","), "Active Capacity"}, []string{brimtext.ThousandsSepU(s.InactiveCapacity, ","), "Inactive Capacity"}, []string{brimtext.ThousandsSep(int64(len(r.Tiers())), ","), "Tier Levels"}, []string{fmt.Sprintf("%.02f%%", s.MaxUnderNodePercentage), fmt.Sprintf("Worst Underweight Node (ID %d)", s.MaxUnderNodeID)}, []string{fmt.Sprintf("%.02f%%", s.MaxOverNodePercentage), fmt.Sprintf("Worst Overweight Node (ID %d)", s.MaxOverNodeID)}, []string{"Version", fmt.Sprintf("%d %s", r.Version(), time.Unix(0, r.Version()).Format("2006-01-02 15:04:05.000"))}, } reportOpts := brimtext.NewDefaultAlignOptions() reportOpts.Alignments = []brimtext.Alignment{brimtext.Right, brimtext.Left} fmt.Fprint(output, brimtext.Align(report, reportOpts)) } if b != nil { var activeNodes int64 var activeCapacity int64 var inactiveNodes int64 var inactiveCapacity int64 for _, n := range b.Nodes() { if n.Active() { activeNodes++ activeCapacity += int64(n.Capacity()) } else { inactiveNodes++ inactiveCapacity += int64(n.Capacity()) } } report := [][]string{ []string{brimtext.ThousandsSep(activeNodes, ","), "Active Nodes"}, []string{brimtext.ThousandsSep(inactiveNodes, ","), "Inactive Nodes"}, []string{brimtext.ThousandsSep(activeCapacity, ","), "Active Capacity"}, []string{brimtext.ThousandsSep(inactiveCapacity, ","), "Inactive Capacity"}, []string{brimtext.ThousandsSep(int64(b.ReplicaCount()), ","), "Replicas"}, []string{brimtext.ThousandsSep(int64(len(b.Tiers())), ","), "Tier Levels"}, []string{brimtext.ThousandsSep(int64(b.PointsAllowed()), ","), "Points Allowed"}, []string{brimtext.ThousandsSep(int64(b.MaxPartitionBitCount()), ","), "Max Partition Bits"}, []string{brimtext.ThousandsSep(int64(b.MoveWait()), ","), "Move Wait"}, []string{brimtext.ThousandsSep(int64(b.IDBits()), ","), "ID Bits"}, } reportOpts := brimtext.NewDefaultAlignOptions() reportOpts.Alignments = []brimtext.Alignment{brimtext.Right, brimtext.Left} fmt.Fprint(output, brimtext.Align(report, reportOpts)) return nil } return nil }
func (s *ValueLocMapStats) String() string { report := [][]string{ {"ActiveCount", fmt.Sprintf("%d", s.ActiveCount)}, {"ActiveBytes", fmt.Sprintf("%d", s.ActiveBytes)}, } if s.statsDebug { depthCounts := fmt.Sprintf("%d", s.depthCounts[0]) for i := 1; i < len(s.depthCounts); i++ { depthCounts += fmt.Sprintf(" %d", s.depthCounts[i]) } report = append(report, [][]string{ {"activePercentage", fmt.Sprintf("%.1f%%", 100*float64(s.ActiveCount)/float64(s.usedEntries))}, {"inactiveMask", fmt.Sprintf("%016x", s.inactiveMask)}, {"workers", fmt.Sprintf("%d", s.workers)}, {"roots", fmt.Sprintf("%d (%d bytes)", s.roots, uint64(s.roots)*uint64(unsafe.Sizeof(valueLocMapNode{})))}, {"usedRoots", fmt.Sprintf("%d", s.usedRoots)}, {"entryPageSize", fmt.Sprintf("%d (%d bytes)", s.entryPageSize, uint64(s.entryPageSize)*uint64(unsafe.Sizeof(valueLocMapEntry{})))}, {"entryLockPageSize", fmt.Sprintf("%d (%d bytes)", s.entryLockPageSize, uint64(s.entryLockPageSize)*uint64(unsafe.Sizeof(sync.RWMutex{})))}, {"splitLevel", fmt.Sprintf("%d +-10%%", s.splitLevel)}, {"nodes", fmt.Sprintf("%d", s.nodes)}, {"depth", fmt.Sprintf("%d", len(s.depthCounts))}, {"depthCounts", depthCounts}, {"allocedEntries", fmt.Sprintf("%d", s.allocedEntries)}, {"allocedInOverflow", fmt.Sprintf("%d %.1f%%", s.allocedInOverflow, 100*float64(s.allocedInOverflow)/float64(s.allocedEntries))}, {"usedEntries", fmt.Sprintf("%d %.1f%%", s.usedEntries, 100*float64(s.usedEntries)/float64(s.allocedEntries))}, {"usedInOverflow", fmt.Sprintf("%d %.1f%%", s.usedInOverflow, 100*float64(s.usedInOverflow)/float64(s.usedEntries))}, {"inactive", fmt.Sprintf("%d %.1f%%", s.inactive, 100*float64(s.inactive)/float64(s.usedEntries))}, }...) } return brimtext.Align(report, nil) }
func printNode(n *pb.Node) { report := [][]string{ []string{"ID:", fmt.Sprintf("%d", n.Id)}, []string{"Active:", fmt.Sprintf("%v", n.Active)}, []string{"Capacity:", fmt.Sprintf("%d", n.Capacity)}, []string{"Tiers:", strings.Join(n.Tiers, "\n")}, []string{"Addresses:", strings.Join(n.Addresses, "\n")}, []string{"Meta:", n.Meta}, []string{"Conf:", string(n.Conf)}, } fmt.Print(brimtext.Align(report, nil)) }
func CLINodeReport(n Node) string { report := [][]string{ []string{"ID:", fmt.Sprintf("%d", n.ID())}, []string{"Active:", fmt.Sprintf("%v", n.Active())}, []string{"Capacity:", fmt.Sprintf("%d", n.Capacity())}, []string{"Tiers:", strings.Join(qStrings(n.Tiers()), "\n")}, []string{"Addresses:", strings.Join(qStrings(n.Addresses()), "\n")}, []string{"Meta:", fmt.Sprintf("%q", n.Meta())}, []string{"Config:", fmt.Sprintf("%q", string(n.Config()))}, } return brimtext.Align(report, nil) }
func (s *SyndClient) setCapacityCmd(id uint64, capacity uint32) error { ctx, _ := context.WithTimeout(context.Background(), 10*time.Second) c, err := s.client.SetCapacity(ctx, &pb.Node{Id: id, Capacity: capacity}) if err != nil { return err } report := [][]string{ []string{"Status:", fmt.Sprintf("%v", c.Status)}, []string{"Version:", fmt.Sprintf("%v", c.Version)}, } fmt.Print(brimtext.Align(report, nil)) return nil }
func (s *SyndClient) setTierCmd(id uint64, tiers []string) error { ctx, _ := context.WithTimeout(context.Background(), 10*time.Second) c, err := s.client.ReplaceTiers(ctx, &pb.Node{Id: id, Tiers: tiers}) if err != nil { return err } report := [][]string{ []string{"Status:", fmt.Sprintf("%v", c.Status)}, []string{"Version:", fmt.Sprintf("%v", c.Version)}, } fmt.Print(brimtext.Align(report, nil)) return nil }
func (s *SyndClient) setReplicasCmd(count int) error { ctx, _ := context.WithTimeout(context.Background(), 10*time.Second) c, err := s.client.SetReplicas(ctx, &pb.RingOpts{Replicas: int32(count)}) if err != nil { return err } report := [][]string{ []string{"Status:", fmt.Sprintf("%v", c.Status)}, []string{"Version:", fmt.Sprintf("%v", c.Version)}, } fmt.Print(brimtext.Align(report, nil)) return nil }
func (s *SyndClient) printConfigCmd() error { ctx, _ := context.WithTimeout(context.Background(), 10*time.Second) c, err := s.client.GetGlobalConfig(ctx, &pb.EmptyMsg{}) if err != nil { return err } report := [][]string{ []string{"Status:", fmt.Sprintf("%v", c.Status.Status)}, []string{"Version:", fmt.Sprintf("%v", c.Status.Version)}, []string{"Conf:", string(c.Conf.Conf)}, } fmt.Print(brimtext.Align(report, nil)) return nil }
// SetConfig sets the global ring config to the provided bytes, and indicates // whether the config change should trigger a restart. func (s *SyndClient) SetConfig(config []byte, restart bool) (err error) { ctx, _ := context.WithTimeout(context.Background(), 10*time.Second) confMsg := &pb.Conf{ Conf: config, RestartRequired: restart, } status, err := s.client.SetConf(ctx, confMsg) if err != nil { return err } report := [][]string{ []string{"Status:", fmt.Sprintf("%v", status.Status)}, []string{"Version:", fmt.Sprintf("%v", status.Version)}, } fmt.Print(brimtext.Align(report, nil)) return nil }
// CLITier outputs a list of tiers in the ring or builder; see the output of // CLIHelp for detailed information. // // Provide either the ring or the builder, but not both; set the other to nil. // Normally the results from RingOrBuilder. func CLITier(r Ring, b *Builder, args []string, output io.Writer) error { var tiers [][]string if r == nil { tiers = b.Tiers() } else { tiers = r.Tiers() } report := [][]string{ []string{"Tier", "Existing"}, []string{"Level", "Values"}, } reportOpts := brimtext.NewDefaultAlignOptions() reportOpts.Alignments = []brimtext.Alignment{brimtext.Right, brimtext.Left} fmted := false OUT: for _, values := range tiers { for _, value := range values { if strings.Contains(value, " ") { fmted = true break OUT } } } for level, values := range tiers { sort.Strings(values) var pvalue string if fmted { for _, value := range values { pvalue += fmt.Sprintf(" %#v", value) } pvalue = strings.Trim(pvalue, " ") } else { pvalue = strings.Join(values, " ") } report = append(report, []string{ strconv.Itoa(level), strings.Trim(pvalue, " "), }) } fmt.Fprint(output, brimtext.Align(report, reportOpts)) return nil }
// CLINode outputs a list of nodes in the ring or builder, with optional // filtering and also allows setting attributes on those nodes; see the output // of CLIHelp for detailed information. // // Provide either the ring or the builder, but not both; set the other to nil. // Normally the results from RingOrBuilder. func CLINode(r Ring, b *Builder, args []string, full bool, output io.Writer) (changed bool, err error) { var nodes NodeSlice if r != nil { nodes = r.Nodes() } else { bnodes := b.Nodes() nodes = make(NodeSlice, len(bnodes)) for i := len(nodes) - 1; i >= 0; i-- { nodes[i] = bnodes[i] } } filterArgs := make([]string, 0) setArgs := make([]string, 0) setFound := false for _, arg := range args { if arg == "set" { setFound = true continue } if setFound { setArgs = append(setArgs, arg) } else { filterArgs = append(filterArgs, arg) } } if len(setArgs) > 0 && b == nil { err = fmt.Errorf("set is only valid for builder files") return } if nodes, err = nodes.Filter(filterArgs); err != nil { return } if len(nodes) > 0 && len(setArgs) > 0 { if r != nil { return false, fmt.Errorf("cannot update nodes in a ring; use with a builder instead") } for _, n := range nodes { if err = CLIAddOrSet(b, setArgs, n.(BuilderNode), output); err != nil { return } changed = true } } if full || len(nodes) == 1 { first := true for _, n := range nodes { if first { first = false } else { fmt.Fprintln(output) } output.Write([]byte(CLINodeReport(n))) } return } header := []string{ "ID", "Active", "Capacity", "Address", "Meta", } report := [][]string{header} reportAlign := brimtext.NewDefaultAlignOptions() reportAlign.Alignments = []brimtext.Alignment{ brimtext.Left, brimtext.Right, brimtext.Right, brimtext.Right, brimtext.Left, } reportLine := func(n Node) []string { return []string{ fmt.Sprintf("%d", n.ID()), fmt.Sprintf("%v", n.Active()), fmt.Sprintf("%d", n.Capacity()), n.Address(0), n.Meta(), } } for _, n := range nodes { if n.Active() { report = append(report, reportLine(n)) } } for _, n := range nodes { if !n.Active() { report = append(report, reportLine(n)) } } fmt.Fprint(output, brimtext.Align(report, reportAlign)) return }
func (stats *ValueStoreStats) String() string { report := [][]string{ {"Values", fmt.Sprintf("%d", stats.Values)}, {"ValueBytes", fmt.Sprintf("%d", stats.ValueBytes)}, {"Lookups", fmt.Sprintf("%d", stats.Lookups)}, {"LookupErrors", fmt.Sprintf("%d", stats.LookupErrors)}, {"Reads", fmt.Sprintf("%d", stats.Reads)}, {"ReadErrors", fmt.Sprintf("%d", stats.ReadErrors)}, {"Writes", fmt.Sprintf("%d", stats.Writes)}, {"WriteErrors", fmt.Sprintf("%d", stats.WriteErrors)}, {"WritesOverridden", fmt.Sprintf("%d", stats.WritesOverridden)}, {"Deletes", fmt.Sprintf("%d", stats.Deletes)}, {"DeleteErrors", fmt.Sprintf("%d", stats.DeleteErrors)}, {"DeletesOverridden", fmt.Sprintf("%d", stats.DeletesOverridden)}, {"OutBulkSets", fmt.Sprintf("%d", stats.OutBulkSets)}, {"OutBulkSetValues", fmt.Sprintf("%d", stats.OutBulkSetValues)}, {"OutBulkSetPushes", fmt.Sprintf("%d", stats.OutBulkSetPushes)}, {"OutBulkSetPushValues", fmt.Sprintf("%d", stats.OutBulkSetPushValues)}, {"InBulkSets", fmt.Sprintf("%d", stats.InBulkSets)}, {"InBulkSetDrops", fmt.Sprintf("%d", stats.InBulkSetDrops)}, {"InBulkSetInvalids", fmt.Sprintf("%d", stats.InBulkSetInvalids)}, {"InBulkSetWrites", fmt.Sprintf("%d", stats.InBulkSetWrites)}, {"InBulkSetWriteErrors", fmt.Sprintf("%d", stats.InBulkSetWriteErrors)}, {"InBulkSetWritesOverridden", fmt.Sprintf("%d", stats.InBulkSetWritesOverridden)}, {"OutBulkSetAcks", fmt.Sprintf("%d", stats.OutBulkSetAcks)}, {"InBulkSetAcks", fmt.Sprintf("%d", stats.InBulkSetAcks)}, {"InBulkSetAckDrops", fmt.Sprintf("%d", stats.InBulkSetAckDrops)}, {"InBulkSetAckInvalids", fmt.Sprintf("%d", stats.InBulkSetAckInvalids)}, {"InBulkSetAckWrites", fmt.Sprintf("%d", stats.InBulkSetAckWrites)}, {"InBulkSetAckWriteErrors", fmt.Sprintf("%d", stats.InBulkSetAckWriteErrors)}, {"InBulkSetAckWritesOverridden", fmt.Sprintf("%d", stats.InBulkSetAckWritesOverridden)}, {"OutPullReplications", fmt.Sprintf("%d", stats.OutPullReplications)}, {"OutPullReplicationNanoseconds", fmt.Sprintf("%d", stats.OutPullReplicationNanoseconds)}, {"InPullReplications", fmt.Sprintf("%d", stats.InPullReplications)}, {"InPullReplicationDrops", fmt.Sprintf("%d", stats.InPullReplicationDrops)}, {"InPullReplicationInvalids", fmt.Sprintf("%d", stats.InPullReplicationInvalids)}, {"ExpiredDeletions", fmt.Sprintf("%d", stats.ExpiredDeletions)}, {"Compactions", fmt.Sprintf("%d", stats.Compactions)}, {"SmallFileCompactions", fmt.Sprintf("%d", stats.SmallFileCompactions)}, {"DiskFree", fmt.Sprintf("%d", stats.DiskFree)}, {"DiskUsed", fmt.Sprintf("%d", stats.DiskUsed)}, {"DiskSize", fmt.Sprintf("%d", stats.DiskSize)}, {"DiskFreeTOC", fmt.Sprintf("%d", stats.DiskFreeTOC)}, {"DiskUsedTOC", fmt.Sprintf("%d", stats.DiskUsedTOC)}, {"DiskSizeTOC", fmt.Sprintf("%d", stats.DiskSizeTOC)}, {"MemFree", fmt.Sprintf("%d", stats.MemFree)}, {"MemUsed", fmt.Sprintf("%d", stats.MemUsed)}, {"MemSize", fmt.Sprintf("%d", stats.MemSize)}, } if stats.debug { report = append(report, [][]string{ nil, {"freeableMemBlockChansCap", fmt.Sprintf("%d", stats.freeableMemBlockChansCap)}, {"freeableMemBlockChansIn", fmt.Sprintf("%d", stats.freeableMemBlockChansIn)}, {"freeMemBlockChanCap", fmt.Sprintf("%d", stats.freeMemBlockChanCap)}, {"freeMemBlockChanIn", fmt.Sprintf("%d", stats.freeMemBlockChanIn)}, {"freeWriteReqChans", fmt.Sprintf("%d", stats.freeWriteReqChans)}, {"freeWriteReqChansCap", fmt.Sprintf("%d", stats.freeWriteReqChansCap)}, {"freeWriteReqChansIn", fmt.Sprintf("%d", stats.freeWriteReqChansIn)}, {"pendingWriteReqChans", fmt.Sprintf("%d", stats.pendingWriteReqChans)}, {"pendingWriteReqChansCap", fmt.Sprintf("%d", stats.pendingWriteReqChansCap)}, {"pendingWriteReqChansIn", fmt.Sprintf("%d", stats.pendingWriteReqChansIn)}, {"fileMemBlockChanCap", fmt.Sprintf("%d", stats.fileMemBlockChanCap)}, {"fileMemBlockChanIn", fmt.Sprintf("%d", stats.fileMemBlockChanIn)}, {"freeTOCBlockChanCap", fmt.Sprintf("%d", stats.freeTOCBlockChanCap)}, {"freeTOCBlockChanIn", fmt.Sprintf("%d", stats.freeTOCBlockChanIn)}, {"pendingTOCBlockChanCap", fmt.Sprintf("%d", stats.pendingTOCBlockChanCap)}, {"pendingTOCBlockChanIn", fmt.Sprintf("%d", stats.pendingTOCBlockChanIn)}, {"maxLocBlockID", fmt.Sprintf("%d", stats.maxLocBlockID)}, {"path", stats.path}, {"pathtoc", stats.pathtoc}, {"workers", fmt.Sprintf("%d", stats.workers)}, {"tombstoneDiscardInterval", fmt.Sprintf("%d", stats.tombstoneDiscardInterval)}, {"outPullReplicationWorkers", fmt.Sprintf("%d", stats.outPullReplicationWorkers)}, {"outPullReplicationInterval", fmt.Sprintf("%d", stats.outPullReplicationInterval)}, {"pushReplicationWorkers", fmt.Sprintf("%d", stats.pushReplicationWorkers)}, {"pushReplicationInterval", fmt.Sprintf("%d", stats.pushReplicationInterval)}, {"valueCap", fmt.Sprintf("%d", stats.valueCap)}, {"pageSize", fmt.Sprintf("%d", stats.pageSize)}, {"minValueAlloc", fmt.Sprintf("%d", stats.minValueAlloc)}, {"writePagesPerWorker", fmt.Sprintf("%d", stats.writePagesPerWorker)}, {"tombstoneAge", fmt.Sprintf("%d", stats.tombstoneAge)}, {"fileCap", fmt.Sprintf("%d", stats.fileCap)}, {"fileReaders", fmt.Sprintf("%d", stats.fileReaders)}, {"checksumInterval", fmt.Sprintf("%d", stats.checksumInterval)}, {"replicationIgnoreRecent", fmt.Sprintf("%d", stats.replicationIgnoreRecent)}, {"locmapDebugInfo", stats.locmapDebugInfo.String()}, }...) } return brimtext.Align(report, nil) }
func main() { if len(os.Args) != 2 { fmt.Printf("%s <dir>\n", os.Args[0]) os.Exit(1) } dirCount, otherCount, fileSizes := dirWalk(os.Args[1], nil) var fileSizesTotal int64 for _, s := range fileSizes { fileSizesTotal += s } fileSizesMean := fileSizesTotal / int64(len(fileSizes)) var fileSizesMedian int64 if len(fileSizes) > 0 { sort.Sort(fileSizes) fileSizesMedian = fileSizes[len(fileSizes)/2] } report := [][]string{ {fmt.Sprintf("%d", dirCount), "directories"}, {fmt.Sprintf("%d", len(fileSizes)), "files"}, {fmt.Sprintf("%d", otherCount), "items not files nor directories"}, {fmt.Sprintf("%d", fileSizesTotal), "total file bytes"}, {fmt.Sprintf("%d", fileSizesMean), "mean file size"}, {fmt.Sprintf("%d", fileSizesMedian), "median file size"}, } alignOptions := brimtext.NewDefaultAlignOptions() alignOptions.Alignments = []brimtext.Alignment{brimtext.Right, brimtext.Left} fmt.Println(brimtext.Align(report, alignOptions)) var blockSizesMean int64 var blockSizesMedian int64 var blockSizesCount int64 var blockSizesFull int64 var blockSizesTarget int64 blockSizesTarget = fileSizesMean var blockSizes int64Slice overshoot := true for { blockSizes = blockSizes[:0] blockSizesFull = 0 for _, s := range fileSizes { if s < 1 { blockSizes = append(blockSizes, 0) continue } for s >= blockSizesTarget { blockSizes = append(blockSizes, blockSizesTarget) s -= blockSizesTarget blockSizesFull++ } if s > 0 { blockSizes = append(blockSizes, s) } } blockSizesCount = int64(len(blockSizes)) blockSizesMean = fileSizesTotal / blockSizesCount if blockSizesCount > 0 { sort.Sort(blockSizes) blockSizesMedian = blockSizes[blockSizesCount/2] } if blockSizesMedian > blockSizesMean && float64(blockSizesMedian-blockSizesMean)/float64(blockSizesMedian) < 0.01 { if !overshoot { break } if blockSizesTarget != math.MaxInt64 { if blockSizesTarget > math.MaxInt64/2 { blockSizesTarget = math.MaxInt64 } else { blockSizesTarget *= 2 } continue } } overshoot = false blockSizesTargetNew := blockSizesTarget - (blockSizesTarget-blockSizesMean+1)/2 if blockSizesTargetNew == blockSizesTarget { break } blockSizesTarget = blockSizesTargetNew } report = [][]string{ {fmt.Sprintf("%d", blockSizesMean), "mean block size"}, {fmt.Sprintf("%d", blockSizesMedian), "median block size"}, {fmt.Sprintf("%d", blockSizesCount), "block count"}, {fmt.Sprintf("%d", blockSizesFull), fmt.Sprintf("full block count, %.0f%%", 100*float64(blockSizesFull)/float64(blockSizesCount))}, {fmt.Sprintf("%d", blockSizesTarget), "\"best\" block size, median~=mean"}, } fmt.Println(brimtext.Align(report, alignOptions)) blockSizesTarget = math.MaxInt64 var blockSizesTargetHighCap int64 var blockSizesTargetLast int64 overshoot = true done := false for { blockSizes = blockSizes[:0] blockSizesFull = 0 for _, s := range fileSizes { if s < 1 { blockSizes = append(blockSizes, 0) continue } for s >= blockSizesTarget { blockSizes = append(blockSizes, blockSizesTarget) s -= blockSizesTarget blockSizesFull++ } if s > 0 { blockSizes = append(blockSizes, s) } } blockSizesCount = int64(len(blockSizes)) blockSizesMean = fileSizesTotal / blockSizesCount if blockSizesCount > 0 { sort.Sort(blockSizes) blockSizesMedian = blockSizes[blockSizesCount/2] } if done { break } if float64(blockSizesFull)/float64(blockSizesCount) < 0.5 { if !overshoot { blockSizesTarget = blockSizesTargetLast done = true continue } blockSizesTargetHighCap = blockSizesTarget blockSizesTarget /= 2 if blockSizesTarget < 1 { blockSizesTarget = 1 done = true } continue } overshoot = false blockSizesTargetNew := blockSizesTarget + (blockSizesTargetHighCap-blockSizesTarget+1)/2 if blockSizesTargetNew == blockSizesTarget { break } blockSizesTargetLast = blockSizesTarget blockSizesTarget = blockSizesTargetNew } report = [][]string{ {fmt.Sprintf("%d", blockSizesMean), "mean block size"}, {fmt.Sprintf("%d", blockSizesMedian), "median block size"}, {fmt.Sprintf("%d", blockSizesCount), "block count"}, {fmt.Sprintf("%d", blockSizesFull), fmt.Sprintf("full block count, %.0f%%", 100*float64(blockSizesFull)/float64(blockSizesCount))}, {fmt.Sprintf("%d", blockSizesTarget), "\"best\" block size, full~=50%"}, } fmt.Println(brimtext.Align(report, alignOptions)) }
func (rend *renderer) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) { opts := &brimtext.AlignOptions{} *opts = *rend.tableAlignOptions opts.Widths = make([]int, len(columnData)) opts.Alignments = make([]brimtext.Alignment, len(columnData)) var data [][]string rows := bytes.Split(header[:len(header)-1], []byte{markTableRow}) for _, row := range rows { var headerRow []string cells := bytes.Split(row[:len(row)-1], []byte{markTableCell}) for c, cell := range cells { if columnData[c]&blackfriday.TABLE_ALIGNMENT_CENTER == blackfriday.TABLE_ALIGNMENT_CENTER { opts.Alignments[c] = brimtext.Center } else if columnData[c]&blackfriday.TABLE_ALIGNMENT_RIGHT != 0 { opts.Alignments[c] = brimtext.Right } cellString := string(cell) if len(cellString) > opts.Widths[c] { opts.Widths[c] = len(cellString) } headerRow = append(headerRow, cellString) } if len(headerRow) > 0 && headerRow[0] != "omit" { data = append(data, headerRow) } } if len(data) > 0 { data = append(data, nil) } rows = bytes.Split(body[:len(body)-1], []byte{markTableRow}) for _, row := range rows { if len(row) == 0 { continue } var bodyRow []string cells := bytes.Split(row[:len(row)-1], []byte{markTableCell}) for c, cell := range cells { cellString := string(cell) if len(cellString) > opts.Widths[c] { opts.Widths[c] = len(cellString) } bodyRow = append(bodyRow, cellString) } data = append(data, bodyRow) } aw := rend.width - rend.currentIndent - len(opts.RowFirstUD) - len(opts.RowLastUD) if len(columnData) > 1 { aw -= len(opts.RowSecondUD) } if len(columnData) > 2 { aw -= len(opts.RowUD)*len(columnData) - 2 } cw := 0 for _, w := range opts.Widths { cw += w } ocw := cw for cw > aw { for i := 0; i < len(opts.Widths); i++ { if opts.Widths[i] > 1 { opts.Widths[i]-- } } cw = 0 for _, w := range opts.Widths { cw += w } if cw == ocw { break } ocw = cw } var text string for { good := true text = brimtext.Align(data, opts) for _, line := range strings.Split(text, "\n") { if len(line) > aw { good = false } } if good { break } ocw = cw for i := 0; i < len(opts.Widths); i++ { if opts.Widths[i] > 1 { opts.Widths[i]-- } } cw = 0 for _, w := range opts.Widths { cw += w } if cw == ocw { break } } textBytes := []byte(text) textBytes = bytes.Replace(textBytes, []byte{' '}, []byte{markNBSP}, -1) textBytes = bytes.Replace(textBytes, []byte{'\n'}, []byte{markLineBreak}, -1) rend.ensureBlankLine(out) out.Write(textBytes) }