github.com/cloudwego/hertz@v0.9.3/pkg/common/stackless/writer.go (about) 1 /* 2 * Copyright 2022 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * The MIT License (MIT) 17 * 18 * Copyright (c) 2015-present Aliaksandr Valialkin, VertaMedia, Kirill Danshin, Erik Dubbelboer, FastHTTP Authors 19 * 20 * Permission is hereby granted, free of charge, to any person obtaining a copy 21 * of this software and associated documentation files (the "Software"), to deal 22 * in the Software without restriction, including without limitation the rights 23 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24 * copies of the Software, and to permit persons to whom the Software is 25 * furnished to do so, subject to the following conditions: 26 * 27 * The above copyright notice and this permission notice shall be included in 28 * all copies or substantial portions of the Software. 29 * 30 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 35 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 36 * THE SOFTWARE. 37 * 38 * This file may have been modified by CloudWeGo authors. All CloudWeGo 39 * Modifications are Copyright 2022 CloudWeGo Authors. 40 */ 41 42 package stackless 43 44 import ( 45 "fmt" 46 "io" 47 48 "github.com/cloudwego/hertz/pkg/common/bytebufferpool" 49 "github.com/cloudwego/hertz/pkg/common/errors" 50 ) 51 52 // Writer is an interface stackless writer must conform to. 53 // 54 // The interface contains common subset for Writers from compress/* packages. 55 type Writer interface { 56 Write(p []byte) (int, error) 57 Flush() error 58 Close() error 59 Reset(w io.Writer) 60 } 61 62 // NewWriterFunc must return new writer that will be wrapped into 63 // stackless writer. 64 type NewWriterFunc func(w io.Writer) Writer 65 66 // NewWriter creates a stackless writer around a writer returned 67 // from newWriter. 68 // 69 // The returned writer writes data to dstW. 70 // 71 // Writers that use a lot of stack space may be wrapped into stackless writer, 72 // thus saving stack space for high number of concurrently running goroutines. 73 func NewWriter(dstW io.Writer, newWriter NewWriterFunc) Writer { 74 w := &writer{ 75 dstW: dstW, 76 } 77 w.zw = newWriter(&w.xw) 78 return w 79 } 80 81 type writer struct { 82 dstW io.Writer 83 zw Writer 84 xw xWriter 85 86 err error 87 n int 88 89 p []byte 90 op op 91 } 92 93 type op int 94 95 const ( 96 opWrite op = iota 97 opFlush 98 opClose 99 opReset 100 ) 101 102 func (w *writer) Write(p []byte) (int, error) { 103 w.p = p 104 err := w.do(opWrite) 105 w.p = nil 106 return w.n, err 107 } 108 109 func (w *writer) Flush() error { 110 return w.do(opFlush) 111 } 112 113 func (w *writer) Close() error { 114 return w.do(opClose) 115 } 116 117 func (w *writer) Reset(dstW io.Writer) { 118 w.xw.Reset() 119 w.do(opReset) //nolint:errcheck 120 w.dstW = dstW 121 } 122 123 func (w *writer) do(op op) error { 124 w.op = op 125 if !stacklessWriterFunc(w) { 126 return errHighLoad 127 } 128 err := w.err 129 if err != nil { 130 return err 131 } 132 if w.xw.bb != nil && len(w.xw.bb.B) > 0 { 133 _, err = w.dstW.Write(w.xw.bb.B) 134 } 135 w.xw.Reset() 136 137 return err 138 } 139 140 var errHighLoad = errors.NewPublic("cannot compress data due to high load") 141 142 var stacklessWriterFunc = NewFunc(writerFunc) 143 144 func writerFunc(ctx interface{}) { 145 w := ctx.(*writer) 146 switch w.op { 147 case opWrite: 148 w.n, w.err = w.zw.Write(w.p) 149 case opFlush: 150 w.err = w.zw.Flush() 151 case opClose: 152 w.err = w.zw.Close() 153 case opReset: 154 w.zw.Reset(&w.xw) 155 w.err = nil 156 default: 157 panic(fmt.Sprintf("BUG: unexpected op: %d", w.op)) 158 } 159 } 160 161 type xWriter struct { 162 bb *bytebufferpool.ByteBuffer 163 } 164 165 func (w *xWriter) Write(p []byte) (int, error) { 166 if w.bb == nil { 167 w.bb = bufferPool.Get() 168 } 169 return w.bb.Write(p) 170 } 171 172 func (w *xWriter) Reset() { 173 if w.bb != nil { 174 bufferPool.Put(w.bb) 175 w.bb = nil 176 } 177 } 178 179 var bufferPool bytebufferpool.Pool