You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							160 lines
						
					
					
						
							4.8 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							160 lines
						
					
					
						
							4.8 KiB
						
					
					
				
								package needle
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"bytes"
							 | 
						|
									"os"
							 | 
						|
									"testing"
							 | 
						|
									"time"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/storage/backend"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/storage/types"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								func TestAppend(t *testing.T) {
							 | 
						|
									n := &Needle{
							 | 
						|
								
							 | 
						|
										Cookie:       types.Cookie(123),   // Cookie Cookie   `comment:"random number to mitigate brute force lookups"`
							 | 
						|
										Id:           types.NeedleId(123), // Id     NeedleId `comment:"needle id"`
							 | 
						|
										Size:         8,                   // Size   uint32   `comment:"sum of DataSize,Data,NameSize,Name,MimeSize,Mime"`
							 | 
						|
										DataSize:     4,                   // DataSize     uint32 `comment:"Data size"` //version2
							 | 
						|
										Data:         []byte("abcd"),      // Data         []byte `comment:"The actual file data"`
							 | 
						|
										Flags:        0,                   // Flags        byte   `comment:"boolean flags"`          //version2
							 | 
						|
										NameSize:     0,                   // NameSize     uint8                                     //version2
							 | 
						|
										Name:         nil,                 // Name         []byte `comment:"maximum 256 characters"` //version2
							 | 
						|
										MimeSize:     0,                   // MimeSize     uint8                                     //version2
							 | 
						|
										Mime:         nil,                 // Mime         []byte `comment:"maximum 256 characters"` //version2
							 | 
						|
										PairsSize:    0,                   // PairsSize    uint16                                    //version2
							 | 
						|
										Pairs:        nil,                 // Pairs        []byte `comment:"additional name value pairs, json format, maximum 6
							 | 
						|
										LastModified: 123,                 // LastModified uint64 //only store LastModifiedBytesLength bytes, which is 5 bytes
							 | 
						|
										Ttl:          nil,                 // Ttl          *TTL
							 | 
						|
										Checksum:     123,                 // Checksum   CRC    `comment:"CRC32 to check integrity"`
							 | 
						|
										AppendAtNs:   123,                 // AppendAtNs uint64 `comment:"append timestamp in nano seconds"` //version3
							 | 
						|
										Padding:      nil,                 // Padding    []byte `comment:"Aligned to 8 bytes"`
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									tempFile, err := os.CreateTemp("", ".dat")
							 | 
						|
									if err != nil {
							 | 
						|
										t.Errorf("Fail TempFile. %v", err)
							 | 
						|
										return
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									/*
							 | 
						|
										uint8  : 0 to 255
							 | 
						|
										uint16 : 0 to 65535
							 | 
						|
										uint32 : 0 to 4294967295
							 | 
						|
										uint64 : 0 to 18446744073709551615
							 | 
						|
										int8   : -128 to 127
							 | 
						|
										int16  : -32768 to 32767
							 | 
						|
										int32  : -2147483648 to 2147483647
							 | 
						|
										int64  : -9223372036854775808 to 9223372036854775807
							 | 
						|
									*/
							 | 
						|
								
							 | 
						|
									fileSize := int64(4294967296) + 10000
							 | 
						|
									tempFile.Truncate(fileSize)
							 | 
						|
									defer func() {
							 | 
						|
										tempFile.Close()
							 | 
						|
										os.Remove(tempFile.Name())
							 | 
						|
									}()
							 | 
						|
								
							 | 
						|
									datBackend := backend.NewDiskFile(tempFile)
							 | 
						|
									defer datBackend.Close()
							 | 
						|
								
							 | 
						|
									offset, _, _, _ := n.Append(datBackend, GetCurrentVersion())
							 | 
						|
									if offset != uint64(fileSize) {
							 | 
						|
										t.Errorf("Fail to Append Needle.")
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func versionString(v Version) string {
							 | 
						|
									switch v {
							 | 
						|
									case Version1:
							 | 
						|
										return "Version1"
							 | 
						|
									case Version2:
							 | 
						|
										return "Version2"
							 | 
						|
									case Version3:
							 | 
						|
										return "Version3"
							 | 
						|
									default:
							 | 
						|
										return "UnknownVersion"
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestWriteNeedle_CompatibilityWithLegacy(t *testing.T) {
							 | 
						|
									versions := []Version{Version1, Version2, Version3}
							 | 
						|
									for _, version := range versions {
							 | 
						|
										t.Run(versionString(version), func(t *testing.T) {
							 | 
						|
											n := &Needle{
							 | 
						|
												Cookie:       0x12345678,
							 | 
						|
												Id:           0x1122334455667788,
							 | 
						|
												Data:         []byte("hello world"),
							 | 
						|
												Flags:        0xFF,
							 | 
						|
												Name:         []byte("filename.txt"),
							 | 
						|
												Mime:         []byte("text/plain"),
							 | 
						|
												LastModified: 0x1234567890,
							 | 
						|
												Ttl:          nil, // Add TTL if needed
							 | 
						|
												Pairs:        []byte("key=value"),
							 | 
						|
												PairsSize:    9,
							 | 
						|
												Checksum:     0xCAFEBABE,
							 | 
						|
												AppendAtNs:   0xDEADBEEF,
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											// Legacy
							 | 
						|
											legacyBuf := &bytes.Buffer{}
							 | 
						|
											_, _, err := n.LegacyPrepareWriteBuffer(version, legacyBuf)
							 | 
						|
											if err != nil {
							 | 
						|
												t.Fatalf("LegacyPrepareWriteBuffer failed: %v", err)
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											// New
							 | 
						|
											newBuf := &bytes.Buffer{}
							 | 
						|
											offset := uint64(0)
							 | 
						|
											switch version {
							 | 
						|
											case Version1:
							 | 
						|
												_, _, err = writeNeedleV1(n, offset, newBuf)
							 | 
						|
											case Version2:
							 | 
						|
												_, _, err = writeNeedleV2(n, offset, newBuf)
							 | 
						|
											case Version3:
							 | 
						|
												_, _, err = writeNeedleV3(n, offset, newBuf)
							 | 
						|
											}
							 | 
						|
											if err != nil {
							 | 
						|
												t.Fatalf("writeNeedleV%d failed: %v", version, err)
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											if !bytes.Equal(legacyBuf.Bytes(), newBuf.Bytes()) {
							 | 
						|
												t.Errorf("Data layout mismatch for version %d\nLegacy: %x\nNew:    %x", version, legacyBuf.Bytes(), newBuf.Bytes())
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								type mockBackendWriter struct {
							 | 
						|
									buf *bytes.Buffer
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (m *mockBackendWriter) WriteAt(p []byte, off int64) (n int, err error) {
							 | 
						|
									return m.buf.Write(p)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (m *mockBackendWriter) GetStat() (int64, time.Time, error) {
							 | 
						|
									return 0, time.Time{}, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (m *mockBackendWriter) Truncate(size int64) error {
							 | 
						|
									return nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (m *mockBackendWriter) Name() string {
							 | 
						|
									return "mock"
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (m *mockBackendWriter) Close() error {
							 | 
						|
									return nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (m *mockBackendWriter) Sync() error {
							 | 
						|
									return nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (m *mockBackendWriter) ReadAt(p []byte, off int64) (n int, err error) {
							 | 
						|
									// Not used in this test
							 | 
						|
									return 0, nil
							 | 
						|
								}
							 |