// StringToImmutableBytes returns a slice of bytes from a string without allocating memory // it is the caller's responsibility not to mutate the bytes returned. func StringToImmutableBytes(s string) []byte { if len(s) == 0 { return nil } // NB(xichen): We need to declare a real byte slice so internally the compiler // knows to use an unsafe.Pointer to keep track of the underlying memory so that // once the slice's array pointer is updated with the pointer to the string's // underlying bytes, the compiler won't prematurely GC the memory when the string // goes out of scope. var b []byte byteHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b)) // NB(xichen): This makes sure that even if GC relocates the string's underlying // memory after this assignment, the corresponding unsafe.Pointer in the internal // slice struct will be updated accordingly to reflect the memory relocation. byteHeader.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data // NB(xichen): It is important that we access s after we assign the Data // pointer of the string header to the Data pointer of the slice header to // make sure the string (and the underlying bytes backing the string) don't get // GC'ed before the assignment happens. l := len(s) byteHeader.Len = l byteHeader.Cap = l return b } func concatSlices(slices ...[]byte) []byte { var totalLen int for _, s := range slices { totalLen += len(s) } tmp := make([]byte, totalLen) var i int for _, s := range slices { i += copy(tmp[i:], s) } return tmp }