github.com/m3db/m3@v1.5.0/src/x/unsafe/string.go (about) 1 // Copyright (c) 2016 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 // Package unsafe contains operations that step around the type safety of Go programs. 22 package unsafe 23 24 import ( 25 "reflect" 26 "unsafe" 27 ) 28 29 // ImmutableBytes represents an immutable byte slice. 30 type ImmutableBytes []byte 31 32 // BytesFn processes a byte slice. 33 type BytesFn func(ImmutableBytes) 34 35 // BytesAndArgFn takes an argument alongside the byte slice. 36 type BytesAndArgFn func(ImmutableBytes, interface{}) 37 38 // WithBytes converts a string to a byte slice with zero heap memory allocations, 39 // and calls a function to process the byte slice. It is the caller's responsibility 40 // to make sure the callback function passed in does not modify the byte slice 41 // in any way, and holds no reference to the byte slice after the function returns. 42 func WithBytes(s string, fn BytesFn) { 43 // NB(xichen): Regardless of whether the backing array is allocated on the heap 44 // or on the stack, it should still be valid before the string goes out of scope 45 // so it's safe to call the function on the underlying byte slice. 46 fn(Bytes(s)) 47 } 48 49 // WithBytesAndArg converts a string to a byte slice with zero heap memory allocations, 50 // and calls a function to process the byte slice alongside one argument. It is the 51 // caller's responsibility to make sure the callback function passed in does not modify 52 // the byte slice in any way, and holds no reference to the byte slice after the function 53 // returns. 54 func WithBytesAndArg(s string, arg interface{}, fn BytesAndArgFn) { 55 fn(Bytes(s), arg) 56 } 57 58 // Bytes returns the bytes backing a string, it is the caller's responsibility 59 // not to mutate the bytes returned. It is much safer to use WithBytes and 60 // WithBytesAndArg if possible, which is more likely to force use of the result 61 // to just a small block of code. 62 func Bytes(s string) ImmutableBytes { 63 if len(s) == 0 { 64 return nil 65 } 66 67 // NB(xichen): We need to declare a real byte slice so internally the compiler 68 // knows to use an unsafe.Pointer to keep track of the underlying memory so that 69 // once the slice's array pointer is updated with the pointer to the string's 70 // underlying bytes, the compiler won't prematurely GC the memory when the string 71 // goes out of scope. 72 var b []byte 73 byteHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b)) 74 75 // NB(xichen): This makes sure that even if GC relocates the string's underlying 76 // memory after this assignment, the corresponding unsafe.Pointer in the internal 77 // slice struct will be updated accordingly to reflect the memory relocation. 78 byteHeader.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data 79 80 // NB(xichen): It is important that we access s after we assign the Data 81 // pointer of the string header to the Data pointer of the slice header to 82 // make sure the string (and the underlying bytes backing the string) don't get 83 // GC'ed before the assignment happens. 84 l := len(s) 85 byteHeader.Len = l 86 byteHeader.Cap = l 87 88 return b 89 }