go 파일 저장소를 사용하는 스택 구현

2020/06/23

쪼렙 스터디차 깔짝깔짝 만들어봄. 아직 만들다 만 것임.

굳이 기본 자료형과 파일 함수를 쓰지않고 돌아돌아 unsafe스럽게 만든 것은 속도 등등을 위한 것.

아직 MT-Safe 하지 않음.

package main

import (
	"unsafe"
	"os"
	"fmt"
	"syscall"
	"reflect"
//	"flag"
	"golang.org/x/sys/unix"
)

const DATA_SIZE	int = 16

type data struct {
//	klen int
	v [DATA_SIZE]byte
}

const INT_SIZE 	int = int(unsafe.Sizeof(int(0)))
const MAX_ITEM	int = 4
const ITEM_SIZE int = int(unsafe.Sizeof(data{}))
const FILE_SIZE	int = ITEM_SIZE * MAX_ITEM

type data_file struct {
	pos1 int
	dummy1 [4096 - INT_SIZE]byte
	items [MAX_ITEM]data
	dummy3 [4096 - (MAX_ITEM * ITEM_SIZE) % 4096]byte
	pos2 int
	dummy2 [4096 - INT_SIZE]byte	
}


func Open_data(init bool, filename string) []data_file {

	size := int(unsafe.Sizeof( data_file{} ))
	println(size)

	var map_file *os.File
	var err error

	if init {
	
		map_file, err = os.Create(filename)
	
	} else {
		map_file, err = os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_SYNC, 0755)
	}
	
	defer map_file.Close()
	
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    _, err = map_file.Seek(int64(size - 1), 0)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    
    _, err = map_file.Write([]byte{0})
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
        
    mmap, err := syscall.Mmap(int(map_file.Fd()), 0, int(size), syscall.PROT_READ | syscall.PROT_WRITE, syscall.MAP_SHARED)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    
    if init {
		for i := range mmap {
			mmap[i] = 0
		}
	}

	header := (*reflect.SliceHeader)(unsafe.Pointer(&mmap))
	header.Len = 1
    header.Cap = 1

    map_data := *(*[]data_file)(unsafe.Pointer(header))
	
	err = map_file.Close()    

    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }    
    
    return map_data
}


func Close_data(mmap []data_file) {
	
	header := (*reflect.SliceHeader)(unsafe.Pointer(&mmap))
	header.Len = int(unsafe.Sizeof(data_file{}))
    header.Cap = header.Len	
	
	unmap := *(*[]byte)(unsafe.Pointer(&mmap))
	err := syscall.Munmap(unmap)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

var sp int = 0
var gpos1 *int
var gpos2 *int
var max_pos *int


func push(item_data []byte, store []data_file ) {
	
	if sp == MAX_ITEM {
		fmt.Fprintf(os.Stderr, "용량 초과, 마지막 푸시 = %s\n", string(item_data))
		return
	}

	copy(store[0].items[*gpos1].v[:], item_data)
	*gpos1++
	*gpos2++
	sp++
	
	
}

func pop(store []data_file ) (ret []byte) {

	sp--
	
	if sp < 0 {
//		ret = []byte("")
		ret = nil
		fmt.Fprintf(os.Stderr, "스택이 비었음\n")
		return 
	}
	
	*gpos1--
	*gpos2--	
	
	ret = store[0].items[*gpos1].v[:]
	
	return
}

func main() {

	println(FILE_SIZE)
	
//	file := flag.String("file", "stack.dat", "스택 저장소 파일 이름")
//	max_items := flag.Int("max-item", MAX_ITEM, "최대 아이템 갯수")
//	bucket_size := flag.Int("bucket-size", DATA_SIZE, "버킷 크기")

	my_data := Open_data(true, "my_data.dat")

	
	header := (*reflect.SliceHeader)(unsafe.Pointer(&my_data[0].pos1))
	header.Len = 4096
	header.Cap = 4096

	unix.Msync(*(*[]byte)(unsafe.Pointer(header)), unix.MS_SYNC)
	
	header = (*reflect.SliceHeader)(unsafe.Pointer(&my_data[0].pos2))
	header.Len = 4096
    header.Cap = 4096

	unix.Msync(*(*[]byte)(unsafe.Pointer(header)), unix.MS_SYNC)	

	gpos1 = &my_data[0].pos1
	gpos2 = &my_data[0].pos2
	
	push([]byte("가나"), my_data)
	push([]byte("다라"), my_data)
	push([]byte("마바"), my_data)
	push([]byte("하하"), my_data)
	push([]byte("호호"), my_data)
	println(string(pop(my_data)))
	println(string(pop(my_data)))
	println(string(pop(my_data)))
	println(string(pop(my_data)))
	println(string(pop(my_data)))
	println(string(pop(my_data)))


	
	defer Close_data(my_data)
	

}