| 
									
										
										
										
											2014-11-02 23:33:06 +01:00
										 |  |  | package rle | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/crypto" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	token             byte = 0xfe | 
					
						
							|  |  |  | 	emptyShaToken          = 0xfd | 
					
						
							|  |  |  | 	emptyListShaToken      = 0xfe | 
					
						
							|  |  |  | 	tokenToken             = 0xff | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var empty = crypto.Sha3([]byte("")) | 
					
						
							|  |  |  | var emptyList = crypto.Sha3([]byte{0x80}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func Decompress(dat []byte) ([]byte, error) { | 
					
						
							|  |  |  | 	buf := new(bytes.Buffer) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := 0; i < len(dat); i++ { | 
					
						
							|  |  |  | 		if dat[i] == token { | 
					
						
							|  |  |  | 			if i+1 < len(dat) { | 
					
						
							|  |  |  | 				switch dat[i+1] { | 
					
						
							|  |  |  | 				case emptyShaToken: | 
					
						
							|  |  |  | 					buf.Write(empty) | 
					
						
							|  |  |  | 				case emptyListShaToken: | 
					
						
							|  |  |  | 					buf.Write(emptyList) | 
					
						
							|  |  |  | 				case tokenToken: | 
					
						
							|  |  |  | 					buf.WriteByte(token) | 
					
						
							|  |  |  | 				default: | 
					
						
							|  |  |  | 					buf.Write(make([]byte, int(dat[i+1]-2))) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				i++ | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				return nil, errors.New("error reading bytes. token encountered without proceeding bytes") | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2014-11-03 00:29:34 +01:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			buf.WriteByte(dat[i]) | 
					
						
							| 
									
										
										
										
											2014-11-02 23:33:06 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return buf.Bytes(), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-03 23:45:32 +01:00
										 |  |  | func compressChunk(dat []byte) (ret []byte, n int) { | 
					
						
							| 
									
										
										
										
											2014-11-03 14:59:50 +01:00
										 |  |  | 	switch { | 
					
						
							|  |  |  | 	case dat[0] == token: | 
					
						
							|  |  |  | 		return []byte{token, tokenToken}, 1 | 
					
						
							|  |  |  | 	case len(dat) > 1 && dat[0] == 0x0 && dat[1] == 0x0: | 
					
						
							|  |  |  | 		j := 0 | 
					
						
							|  |  |  | 		for j <= 254 && j < len(dat) { | 
					
						
							|  |  |  | 			if dat[j] != 0 { | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			j++ | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return []byte{token, byte(j + 2)}, j | 
					
						
							|  |  |  | 	case len(dat) >= 32: | 
					
						
							|  |  |  | 		if dat[0] == empty[0] && bytes.Compare(dat[:32], empty) == 0 { | 
					
						
							|  |  |  | 			return []byte{token, emptyShaToken}, 32 | 
					
						
							|  |  |  | 		} else if dat[0] == emptyList[0] && bytes.Compare(dat[:32], emptyList) == 0 { | 
					
						
							|  |  |  | 			return []byte{token, emptyListShaToken}, 32 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		fallthrough | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return dat[:1], 1 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-02 23:33:06 +01:00
										 |  |  | func Compress(dat []byte) []byte { | 
					
						
							|  |  |  | 	buf := new(bytes.Buffer) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-03 14:59:50 +01:00
										 |  |  | 	i := 0 | 
					
						
							|  |  |  | 	for i < len(dat) { | 
					
						
							| 
									
										
										
										
											2014-11-03 23:45:32 +01:00
										 |  |  | 		b, n := compressChunk(dat[i:]) | 
					
						
							| 
									
										
										
										
											2014-11-03 14:59:50 +01:00
										 |  |  | 		buf.Write(b) | 
					
						
							|  |  |  | 		i += n | 
					
						
							| 
									
										
										
										
											2014-11-02 23:33:06 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return buf.Bytes() | 
					
						
							|  |  |  | } |