Browse Source

remove the need for a temp buffer

flashmob 6 years ago
parent
commit
6b9559528e
3 changed files with 21 additions and 15 deletions
  1. 2 2
      chunk/chunk_test.go
  2. 18 12
      chunk/decoder.go
  3. 1 1
      tests/pidfilex.pid

+ 2 - 2
chunk/chunk_test.go

@@ -398,7 +398,7 @@ func TestChunkSaverReader(t *testing.T) {
 		}
 		}
 		result.Reset()
 		result.Reset()
 
 
-		// test the decoder, hit the decoderStateFindHeader state
+		// test the decoder, hit the decoderStateFindHeaderEnd state
 		r, err = NewChunkedReader(store, email, 0)
 		r, err = NewChunkedReader(store, email, 0)
 		if err != nil {
 		if err != nil {
 			t.Error(err)
 			t.Error(err)
@@ -415,7 +415,7 @@ func TestChunkSaverReader(t *testing.T) {
 			t.FailNow()
 			t.FailNow()
 		}
 		}
 
 
-		buf4 := make([]byte, 64) // state decoderStateFindHeader will hit
+		buf4 := make([]byte, 64) // state decoderStateFindHeaderEnd will hit
 		_, err = io.CopyBuffer(&result, dr, buf4)
 		_, err = io.CopyBuffer(&result, dr, buf4)
 		if err != nil {
 		if err != nil {
 			t.Error()
 			t.Error()

+ 18 - 12
chunk/decoder.go

@@ -23,14 +23,14 @@ const (
 
 
 // decoder decodes base64 and q-printable, then converting charset to UTF-8
 // decoder decodes base64 and q-printable, then converting charset to UTF-8
 type decoder struct {
 type decoder struct {
-	buf       []byte
 	state     int
 	state     int
 	charset   string
 	charset   string
 	transport transportEncoding
 	transport transportEncoding
 	r         io.Reader
 	r         io.Reader
 }
 }
 
 
-// NewDecoder reads from an underlying reader r and decodes base64, quoted-printable and decodes
+// NewDecoder reads a MIME-part from an underlying reader r
+// then decodes base64 or quoted-printable to 8bit, and converts encoding to UTF-8
 func NewDecoder(r io.Reader, transport transportEncoding, charset string) (*decoder, error) {
 func NewDecoder(r io.Reader, transport transportEncoding, charset string) (*decoder, error) {
 	decoder := new(decoder)
 	decoder := new(decoder)
 	decoder.transport = transport
 	decoder.transport = transport
@@ -42,48 +42,52 @@ func NewDecoder(r io.Reader, transport transportEncoding, charset string) (*deco
 const chunkSaverNL = '\n'
 const chunkSaverNL = '\n'
 
 
 const (
 const (
-	decoderStateFindHeader int = iota
+	decoderStateFindHeaderEnd int = iota
 	decoderStateMatchNL
 	decoderStateMatchNL
 	decoderStateDecodeSetup
 	decoderStateDecodeSetup
 	decoderStateDecode
 	decoderStateDecode
 )
 )
 
 
 func (r *decoder) Read(p []byte) (n int, err error) {
 func (r *decoder) Read(p []byte) (n int, err error) {
-	r.buf = make([]byte, len(p), cap(p))
 	var start, buffered int
 	var start, buffered int
 	if r.state != decoderStateDecode {
 	if r.state != decoderStateDecode {
-		buffered, err = r.r.Read(r.buf)
+		// we haven't found the body yet, so read in some input and scan for the end of the header
+		// i.e. the body starts after "\n\n" is matched
+		buffered, err = r.r.Read(p)
 		if buffered == 0 {
 		if buffered == 0 {
 			return
 			return
 		}
 		}
 	}
 	}
+	buf := p[0:buffered] // we'll use p as a scratch buffer
 	for {
 	for {
+		// The following is a simple state machine to find the body. Once it's found, the machine will go into a
+		// 'decoderStateDecode' state where the decoding will be performed with each call to Read
 		switch r.state {
 		switch r.state {
-		case decoderStateFindHeader:
+		case decoderStateFindHeaderEnd:
 			// finding the start of the header
 			// finding the start of the header
-			if start = bytes.Index(r.buf, []byte{chunkSaverNL, chunkSaverNL}); start != -1 {
+			if start = bytes.Index(buf, []byte{chunkSaverNL, chunkSaverNL}); start != -1 {
 				start += 2                        // skip the \n\n
 				start += 2                        // skip the \n\n
 				r.state = decoderStateDecodeSetup // found the header
 				r.state = decoderStateDecodeSetup // found the header
 				continue
 				continue
-			} else if r.buf[len(r.buf)-1] == chunkSaverNL {
+			} else if buf[len(buf)-1] == chunkSaverNL {
 				// the last char is a \n so next call to Read will check if it starts with a matching \n
 				// the last char is a \n so next call to Read will check if it starts with a matching \n
 				r.state = decoderStateMatchNL
 				r.state = decoderStateMatchNL
 			}
 			}
 		case decoderStateMatchNL:
 		case decoderStateMatchNL:
 			// check the first char if it is a '\n' because last time we matched a '\n'
 			// check the first char if it is a '\n' because last time we matched a '\n'
-			if r.buf[0] == '\n' {
+			if buf[0] == '\n' {
 				// found the header
 				// found the header
 				start = 1
 				start = 1
 				r.state = decoderStateDecodeSetup
 				r.state = decoderStateDecodeSetup
 				continue
 				continue
 			} else {
 			} else {
-				r.state = decoderStateFindHeader
+				r.state = decoderStateFindHeaderEnd
 				continue
 				continue
 			}
 			}
 		case decoderStateDecodeSetup:
 		case decoderStateDecodeSetup:
 			if start != buffered {
 			if start != buffered {
 				// include any bytes that have already been read
 				// include any bytes that have already been read
-				r.r = io.MultiReader(bytes.NewBuffer(r.buf[start:buffered]), r.r)
+				r.r = io.MultiReader(bytes.NewBuffer(buf[start:]), r.r)
 			}
 			}
 			switch r.transport {
 			switch r.transport {
 			case transportQuotedPrintable:
 			case transportQuotedPrintable:
@@ -103,10 +107,12 @@ func (r *decoder) Read(p []byte) (n int, err error) {
 			r.state = decoderStateDecode
 			r.state = decoderStateDecode
 			continue
 			continue
 		case decoderStateDecode:
 		case decoderStateDecode:
+			// already found the body, do conversion and decoding
 			return r.r.Read(p)
 			return r.r.Read(p)
 		}
 		}
 		start = 0
 		start = 0
-		buffered, err = r.r.Read(r.buf)
+		// haven't found the body yet, continue scanning
+		buffered, err = r.r.Read(buf)
 		if buffered == 0 {
 		if buffered == 0 {
 			return
 			return
 		}
 		}

+ 1 - 1
tests/pidfilex.pid

@@ -1 +1 @@
-30534
+1959