Octets Golang.Analyse du code source du tampon
En tant que cache efficace, Buffer prend en charge les opérations de lecture, d'écriture et d'annulation de lecture, et peut être utilisé pour la mise en cache et l'analyse de flux d'octets. Voici une brève analyse de son implémentation du code source.
La version source de cet article est go1.16.3 linux/amd64
règle d'auto-croissance
Le tampon peut augmenter sa propre longueur lorsque la longueur est insuffisante. Sa fonction de croissance principale est grow(n int) int
une fonction privée et le code source se trouve à go/src/bytes/buffer.go
la ligne 117 :
// grow grows the buffer to guarantee space for n more bytes.
// It returns the index where bytes should be written.
// If the buffer can't grow it will panic with ErrTooLarge.
func (b *Buffer) grow(n int) int {
m := b.Len()
// If buffer is empty, reset to recover space.
if m == 0 && b.off != 0 {
b.Reset()
}
// Try to grow by means of a reslice.
if i, ok := b.tryGrowByReslice(n); ok {
return i
}
if b.buf == nil && n <= smallBufferSize {
b.buf = make([]byte, n, smallBufferSize)
return 0
}
c := cap(b.buf)
if n <= c/2-m {
// We can slide things down instead of allocating a new
// slice. We only need m+n <= c to slide, but
// we instead let capacity get twice as large so we
// don't spend all our time copying.
copy(b.buf, b.buf[b.off:])
} else if c > maxInt-c-n {
panic(ErrTooLarge)
} else {
// Not enough space anywhere, we need to allocate.
buf := makeSlice(2*c + n)
copy(buf, b.buf[b.off:])
b.buf = buf
}
// Restore b.off and len(b.buf).
b.off = 0
b.buf = b.buf[:m+n]
return m
}
Parmi eux, tryGrowByReslice
la fonction de est principalement d' initialiser un nouvel espace de n octets pour que la longueur respecte la norme lorsque la b.buf
capacité répond à la demande mais que la longueur ne répond pas à la demande. Le code source est le suivant :len
// tryGrowByReslice is a inlineable version of grow for the fast-case where the
// internal buffer only needs to be resliced.
// It returns the index where bytes should be written and whether it succeeded.
func (b *Buffer) tryGrowByReslice(n int) (int, bool) {
if l := len(b.buf); n <= cap(b.buf)-l {
b.buf = b.buf[:l+n]
return l, true
}
return 0, false
}
Au lieu de cela , il a simplement été ajouté avant makeSlice
et le code source n'a pas été publié.make([]byte, n)
defer
On voit que grow(n)
la fonction de est de s'assurer que le cache peut libérer au moins n octets d'espace libre. La fonction est d'abord appelée tryGrowByReslice
pour déterminer b.buf
si l'espace restant répond aux exigences et initialiser l'espace d'octets de n bits ; sinon, essayez de réorganiser les éléments existants pour obtenir suffisamment d'espace ; si le réarrangement ne peut pas résoudre le problème, alors Réallouez un plus grand bloc de mémoire.
En raison de grow
ce mécanisme de la fonction, Buffer est similaire à une file d'attente circulaire auto-croissante dans certaines caractéristiques, son "pointeur arrière" est équivalent len(b.buf)
et son "pointeur avant" est équivalent b.off
. Lorsqu'une opération d'écriture (équivalente à entrer dans la file d'attente), len(b.buf)
il grandit ; lorsqu'une opération de lecture se produit (équivalent à sortir de la file d'attente), b.off
il avance ; contrairement à la file d'attente circulaire, lorsque le Buffer est sur le point de déborder faussement, le "pointeur arrière" ne ne pas se déplacer vers la tête Au lieu de reboucler à la tête, b.off
tous les éléments inutilisés sont glissés du début vers la tête. Bien que la complexité temporelle O(m) de l'opération de traduction globale soit supérieure à celle de la boucle de pointeur O(1), parce que copy
la fonction est une fonction intégrée à la couche inférieure, cela ne prend en fait pas beaucoup de temps ; et dans Afin de réduire davantage la surcharge causée par la traduction fréquente, le critère de "faux débordement" n'est pas celui actuel cap
, mais cap
le double de celui actuel. Ici, nous pouvons également voir la logique de conception de golang, c'est-à-dire que, sous l'hypothèse que le matériel informatique moderne est suffisamment développé, le développement de logiciels peut être grandement facilité en sacrifiant de manière appropriée la surcharge de ressources, ce qui est tout le contraire de l'idée de conception de C /C++.
lire et annuler la lecture
Buffer prend en charge Read
, ReadByte
et ReadRune
trois opérations de lecture majeures, qui Read
sont irréversibles ReadByte
et ReadRune
peuvent être annulées une fois.
Après avoir lu n octets, Buffer avancera b.off
de n octets. La soi-disant annulation consiste essentiellement à b.off
ramener les données à la position avant l'opération de lecture. En termes d'implémentation spécifique, UnreadByte
oui b.off--
, UnreadRune
c'est un b.lastRead
octet avant.
Voici un exemple simple d'application pour annuler la lecture : lors de la transmission de données en continu, vous devez trouver l'en-tête de synchronisation, vous pouvez lire un octet et le comparer, s'il s'agit d'un en-tête de synchronisation, continuez à lire ; si ce n'est pas une synchronisation en-tête, annulez la lecture, afin que les données soient conservées telles quelles et transmises au lien suivant qui a besoin des données.
Tronquer et réinitialiser
Buffer.Truncate(n int)
Le cache peut être tronqué pour ne garder que ce qui est nécessaire. Cette fonction synchronisera les propriétés modifiéesb.buf
et . Le code source est le suivant :len
cap
// Truncate discards all but the first n unread bytes from the buffer
// but continues to use the same allocated storage.
// It panics if n is negative or greater than the length of the buffer.
func (b *Buffer) Truncate(n int) {
if n == 0 {
b.Reset()
return
}
b.lastRead = opInvalid
if n < 0 || n > b.Len() {
panic("bytes.Buffer: truncation out of range")
}
b.buf = b.buf[:b.off+n]
}
Buffer.Reset()
Va complètement réinitialiserb.buf
etb.off
, son code source est très simple :
// Reset resets the buffer to be empty,
// but it retains the underlying storage for use by future writes.
// Reset is the same as Truncate(0).
func (b *Buffer) Reset() {
b.buf = b.buf[:0]
b.off = 0
b.lastRead = opInvalid
}