mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 18:13:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			185 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			185 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2013 Google Inc. All Rights Reserved.
 | 
						|
// Copyright 2022 The Kubernetes Authors.
 | 
						|
//
 | 
						|
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
// you may not use this file except in compliance with the License.
 | 
						|
// You may obtain a copy of the License at
 | 
						|
//
 | 
						|
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
//
 | 
						|
// Unless required by applicable law or agreed to in writing, software
 | 
						|
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
// See the License for the specific language governing permissions and
 | 
						|
// limitations under the License.
 | 
						|
 | 
						|
// Package buffer provides a cache for byte.Buffer instances that can be reused
 | 
						|
// to avoid frequent allocation and deallocation. It also has utility code
 | 
						|
// for log header formatting that use these buffers.
 | 
						|
package buffer
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"os"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"k8s.io/klog/v2/internal/severity"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	// Pid is inserted into log headers. Can be overridden for tests.
 | 
						|
	Pid = os.Getpid()
 | 
						|
 | 
						|
	// Time, if set, will be used instead of the actual current time.
 | 
						|
	Time *time.Time
 | 
						|
)
 | 
						|
 | 
						|
// Buffer holds a single byte.Buffer for reuse. The zero value is ready for
 | 
						|
// use. It also provides some helper methods for output formatting.
 | 
						|
type Buffer struct {
 | 
						|
	bytes.Buffer
 | 
						|
	Tmp [64]byte // temporary byte array for creating headers.
 | 
						|
}
 | 
						|
 | 
						|
var buffers = sync.Pool{
 | 
						|
	New: func() interface{} {
 | 
						|
		return new(Buffer)
 | 
						|
	},
 | 
						|
}
 | 
						|
 | 
						|
// GetBuffer returns a new, ready-to-use buffer.
 | 
						|
func GetBuffer() *Buffer {
 | 
						|
	b := buffers.Get().(*Buffer)
 | 
						|
	b.Reset()
 | 
						|
	return b
 | 
						|
}
 | 
						|
 | 
						|
// PutBuffer returns a buffer to the free list.
 | 
						|
func PutBuffer(b *Buffer) {
 | 
						|
	if b.Len() >= 256 {
 | 
						|
		// Let big buffers die a natural death, without relying on
 | 
						|
		// sync.Pool behavior. The documentation implies that items may
 | 
						|
		// get deallocated while stored there ("If the Pool holds the
 | 
						|
		// only reference when this [= be removed automatically]
 | 
						|
		// happens, the item might be deallocated."), but
 | 
						|
		// https://github.com/golang/go/issues/23199 leans more towards
 | 
						|
		// having such a size limit.
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	buffers.Put(b)
 | 
						|
}
 | 
						|
 | 
						|
// Some custom tiny helper functions to print the log header efficiently.
 | 
						|
 | 
						|
const digits = "0123456789"
 | 
						|
 | 
						|
// twoDigits formats a zero-prefixed two-digit integer at buf.Tmp[i].
 | 
						|
func (buf *Buffer) twoDigits(i, d int) {
 | 
						|
	buf.Tmp[i+1] = digits[d%10]
 | 
						|
	d /= 10
 | 
						|
	buf.Tmp[i] = digits[d%10]
 | 
						|
}
 | 
						|
 | 
						|
// nDigits formats an n-digit integer at buf.Tmp[i],
 | 
						|
// padding with pad on the left.
 | 
						|
// It assumes d >= 0.
 | 
						|
func (buf *Buffer) nDigits(n, i, d int, pad byte) {
 | 
						|
	j := n - 1
 | 
						|
	for ; j >= 0 && d > 0; j-- {
 | 
						|
		buf.Tmp[i+j] = digits[d%10]
 | 
						|
		d /= 10
 | 
						|
	}
 | 
						|
	for ; j >= 0; j-- {
 | 
						|
		buf.Tmp[i+j] = pad
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// someDigits formats a zero-prefixed variable-width integer at buf.Tmp[i].
 | 
						|
func (buf *Buffer) someDigits(i, d int) int {
 | 
						|
	// Print into the top, then copy down. We know there's space for at least
 | 
						|
	// a 10-digit number.
 | 
						|
	j := len(buf.Tmp)
 | 
						|
	for {
 | 
						|
		j--
 | 
						|
		buf.Tmp[j] = digits[d%10]
 | 
						|
		d /= 10
 | 
						|
		if d == 0 {
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return copy(buf.Tmp[i:], buf.Tmp[j:])
 | 
						|
}
 | 
						|
 | 
						|
// FormatHeader formats a log header using the provided file name and line number
 | 
						|
// and writes it into the buffer.
 | 
						|
func (buf *Buffer) FormatHeader(s severity.Severity, file string, line int, now time.Time) {
 | 
						|
	if line < 0 {
 | 
						|
		line = 0 // not a real line number, but acceptable to someDigits
 | 
						|
	}
 | 
						|
	if s > severity.FatalLog {
 | 
						|
		s = severity.InfoLog // for safety.
 | 
						|
	}
 | 
						|
 | 
						|
	// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
 | 
						|
	// It's worth about 3X. Fprintf is hard.
 | 
						|
	if Time != nil {
 | 
						|
		now = *Time
 | 
						|
	}
 | 
						|
	_, month, day := now.Date()
 | 
						|
	hour, minute, second := now.Clock()
 | 
						|
	// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
 | 
						|
	buf.Tmp[0] = severity.Char[s]
 | 
						|
	buf.twoDigits(1, int(month))
 | 
						|
	buf.twoDigits(3, day)
 | 
						|
	buf.Tmp[5] = ' '
 | 
						|
	buf.twoDigits(6, hour)
 | 
						|
	buf.Tmp[8] = ':'
 | 
						|
	buf.twoDigits(9, minute)
 | 
						|
	buf.Tmp[11] = ':'
 | 
						|
	buf.twoDigits(12, second)
 | 
						|
	buf.Tmp[14] = '.'
 | 
						|
	buf.nDigits(6, 15, now.Nanosecond()/1000, '0')
 | 
						|
	buf.Tmp[21] = ' '
 | 
						|
	buf.nDigits(7, 22, Pid, ' ') // TODO: should be TID
 | 
						|
	buf.Tmp[29] = ' '
 | 
						|
	buf.Write(buf.Tmp[:30])
 | 
						|
	buf.WriteString(file)
 | 
						|
	buf.Tmp[0] = ':'
 | 
						|
	n := buf.someDigits(1, line)
 | 
						|
	buf.Tmp[n+1] = ']'
 | 
						|
	buf.Tmp[n+2] = ' '
 | 
						|
	buf.Write(buf.Tmp[:n+3])
 | 
						|
}
 | 
						|
 | 
						|
// SprintHeader formats a log header and returns a string. This is a simpler
 | 
						|
// version of FormatHeader for use in ktesting.
 | 
						|
func (buf *Buffer) SprintHeader(s severity.Severity, now time.Time) string {
 | 
						|
	if s > severity.FatalLog {
 | 
						|
		s = severity.InfoLog // for safety.
 | 
						|
	}
 | 
						|
 | 
						|
	// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
 | 
						|
	// It's worth about 3X. Fprintf is hard.
 | 
						|
	if Time != nil {
 | 
						|
		now = *Time
 | 
						|
	}
 | 
						|
	_, month, day := now.Date()
 | 
						|
	hour, minute, second := now.Clock()
 | 
						|
	// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
 | 
						|
	buf.Tmp[0] = severity.Char[s]
 | 
						|
	buf.twoDigits(1, int(month))
 | 
						|
	buf.twoDigits(3, day)
 | 
						|
	buf.Tmp[5] = ' '
 | 
						|
	buf.twoDigits(6, hour)
 | 
						|
	buf.Tmp[8] = ':'
 | 
						|
	buf.twoDigits(9, minute)
 | 
						|
	buf.Tmp[11] = ':'
 | 
						|
	buf.twoDigits(12, second)
 | 
						|
	buf.Tmp[14] = '.'
 | 
						|
	buf.nDigits(6, 15, now.Nanosecond()/1000, '0')
 | 
						|
	buf.Tmp[21] = ']'
 | 
						|
	return string(buf.Tmp[:22])
 | 
						|
}
 |