github.com/m3db/m3@v1.5.0/src/x/unsafe/bytes.go (about) 1 // Copyright (c) 2019 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 // StringFn processes a byte slice. 30 type StringFn func(string) 31 32 // StringAndArgFn takes an argument alongside the byte slice. 33 type StringAndArgFn func(string, interface{}) 34 35 // WithString converts a byte slice to a string with zero heap memory 36 // allocations, and calls a function to process the string. It is the caller's 37 // responsibility to make sure it holds no reference to the string after the 38 // function returns. 39 func WithString(b []byte, fn StringFn) { 40 // NB(r): regardless of whether the backing array is allocated on the heap 41 // or on the stack, it should still be valid before the byte slice goes out of scope 42 // so it's safe to call the function on the underlying byte slice. 43 fn(String(b)) 44 } 45 46 // WithStringAndArg converts a byte slice to a string with zero heap memory 47 // allocations, and calls a function to process the string with one argument. 48 // It is the caller's responsibility to make sure it holds no reference to the 49 // string after the function returns. 50 func WithStringAndArg(b []byte, arg interface{}, fn StringAndArgFn) { 51 fn(String(b), arg) 52 } 53 54 // String returns a string backed by a byte slice, it is the caller's 55 // responsibility not to mutate the bytes while using the string returned. It 56 // is much safer to use WithString and WithStringAndArg if possible, which is 57 // more likely to force use of the result to just a small block of code. 58 func String(b []byte) string { 59 var s string 60 if len(b) == 0 { 61 return s 62 } 63 64 // NB(r): We need to declare a real string so internally the compiler 65 // knows to use an unsafe.Pointer to keep track of the underlying memory so that 66 // once the strings's array pointer is updated with the pointer to the byte slices's 67 // underlying bytes, the compiler won't prematurely GC the memory when the byte slice 68 // goes out of scope. 69 stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&s)) 70 71 // NB(r): This makes sure that even if GC relocates the byte slices's underlying 72 // memory after this assignment, the corresponding unsafe.Pointer in the internal 73 // string struct will be updated accordingly to reflect the memory relocation. 74 stringHeader.Data = (*reflect.SliceHeader)(unsafe.Pointer(&b)).Data 75 76 // NB(r): It is important that we access b after we assign the Data 77 // pointer of the byte slice header to the Data pointer of the string header to 78 // make sure the bytes don't get GC'ed before the assignment happens. 79 l := len(b) 80 stringHeader.Len = l 81 82 return s 83 }