feat: In-progress work on unity files
This commit is contained in:
		
							
								
								
									
										93
									
								
								storage/unity_file.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								storage/unity_file.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					package storage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"git.nicholasnovak.io/nnovak/spatial-db/world"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// A `UnityFile` is a collection of chunks, stored as a single file on disk
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This is done because with worlds that span millions of chunks, the speed to
 | 
				
			||||||
 | 
					// access individual files slows down
 | 
				
			||||||
 | 
					type UnityFile struct {
 | 
				
			||||||
 | 
						fd       *os.File
 | 
				
			||||||
 | 
						fileSize int
 | 
				
			||||||
 | 
						// metadata maps the position of a chunk to its start index within the file
 | 
				
			||||||
 | 
						metadata map[world.ChunkPos]int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func CreateUnityFile(fileName string) (UnityFile, error) {
 | 
				
			||||||
 | 
						var u UnityFile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f, err := os.Create(fileName)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return u, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u.fd = f
 | 
				
			||||||
 | 
						u.metadata = make(map[world.ChunkPos]int)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return u, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u UnityFile) Size() int {
 | 
				
			||||||
 | 
						return u.fileSize
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *UnityFile) WriteChunk(data world.ChunkData) error {
 | 
				
			||||||
 | 
						var encoded bytes.Buffer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Encode the chunk first
 | 
				
			||||||
 | 
						if err := json.NewEncoder(&encoded).Encode(data); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						encodedSize := encoded.Len()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Go to the end of the file
 | 
				
			||||||
 | 
						u.fd.Seek(0, u.fileSize)
 | 
				
			||||||
 | 
						// Write the encoded contents to the file
 | 
				
			||||||
 | 
						if _, err := u.fd.Write(encoded.Bytes()); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Update the metadata with the new file
 | 
				
			||||||
 | 
						u.metadata[data.Pos] = u.fileSize
 | 
				
			||||||
 | 
						u.fileSize += encodedSize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u UnityFile) WriteMetadataFile(fileName string) error {
 | 
				
			||||||
 | 
						fd, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer fd.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := json.NewEncoder(fd).Encode(&u.metadata); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *UnityFile) ReadMetadataFile(fileName string) error {
 | 
				
			||||||
 | 
						fd, err := os.Open(fileName)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := json.NewDecoder(fd).Decode(&u.metadata); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u UnityFile) ReadChunk() world.ChunkData {
 | 
				
			||||||
 | 
						return world.ChunkData{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								storage/unity_file_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								storage/unity_file_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					package storage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCreateUnityFile(t *testing.T) {
 | 
				
			||||||
 | 
						tempDir, err := os.MkdirTemp("", "unity")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Error creating temporary directory: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer os.RemoveAll(tempDir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create an empty file
 | 
				
			||||||
 | 
						u, err := CreateUnityFile(path.Join(tempDir, "test-unity"))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Error creating unity file: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if u.Size() != 0 {
 | 
				
			||||||
 | 
							t.Fatalf("Expected size of file to be %v, got %v", 0, u.Size())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Save the metadata
 | 
				
			||||||
 | 
						if err := u.WriteMetadataFile(path.Join(tempDir, "test-unity.metadata")); err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Got an error saving the empty metadata: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user