Skip to content

Latest commit

 

History

History
135 lines (100 loc) · 4.44 KB

reader-in-few-lines-with-endian-ness.md

File metadata and controls

135 lines (100 loc) · 4.44 KB

with mojo v0.4.0

Theses mojo features are used:

It is nice to see how they work together.

note: how productive and user friendly 🔥 is

⚠️ the code has not been tested, not ready for use

def main():
    var reader = file_reader("data_from_ruby")
    
    #return a Pointer to four Uint32
    let LE_ui32 = reader.read[UInt32](4)

    for i in range(4):
        print(LE_ui32.load(i))

    #two Float32, converted from another endian-ness
    
    let BE_f32 = reader.read[Float32,"swap"](2)
    
    print(BE_f32.load(0))
    print(BE_f32.load(1))
    
    #free the pointers
    LE_ui32.free()
    BE_f32.free()

    #move the reader cursor back four bytes
    reader.offset -=4

    #Float32 (not pointer), converted from another endian-ness
    print(reader.read_one[Float32,"swap"]())
from sys.info import sizeof

struct file_reader:
    var data:DTypePointer[DType.uint8]
    var size: Int
    var offset:Int

    fn __init__(inout self,filename:String) raises:
        #open the file
        var handler = open(filename,"r")

        #pointer with no allocation done:
        self.data = DTypePointer[DType.uint8]()
        self.size = 0
        self.offset = 0
        
        #read the file into tmp
        let tmp = handler.read()
        self.size = tmp._buffer.size
        self.data = self.data.alloc(self.size)
        
        #interpret the bytes as uint8 when .load(index)
        let tmp_ptr = tmp._as_ptr().bitcast[DType.uint8]()
        
        #copy the content of tmp into self.data
        for i in range(self.size):
            self.data.store(i,tmp_ptr.load(i))
        
        _=tmp #extending the lifetime to more than tmp_ptr
        #if dont do, tmp will be freed when last used
        #and tmp_ptr will point to freed memory
        
        #close the file
        handler.close()
    
    fn read_one[T:AnyType,endian:StringLiteral= "not_swap"](inout self) raises->T:
        let tmp = self.read[T,endian](1)
        let tmp2:T = tmp.load(0)
        tmp.free()
        return tmp2
    
    #"be" is default if no value is passed
    fn read[T:AnyType,endian:StringLiteral= "not_swap"](inout self,e: Int = 1) raises->Pointer[T]:
        
        #size of T in bytes multiplied by elements
        let fsize = e*sizeof[T]()
        
        if (self.offset+fsize)>self.size:
            raise Error("file is not that big")
        
        let tmp = Pointer[UInt8]().alloc(fsize)
        
        for i in range(fsize):
            tmp.store(i,self.data.load(i+self.offset))
        
        self.offset+=fsize
        
        #swapping the bytes if specified:
        @parameter
        #"not_swap" is the default parameter value!
        if endian == "swap":
            alias sizeoft=sizeof[T]()

            let tmp_for_swap = Pointer[UInt8]().alloc(sizeoft)
            
            for i in range(0,fsize,sizeoft):
                for i2 in range(0,sizeoft):
                    tmp_for_swap.store(i2,tmp.load(i+i2))
                for i2 in range(0,sizeoft):
                    tmp.store(i+i2,tmp_for_swap.load(sizeoft-1-i2))
            
            tmp_for_swap.free()
        #Pointer[UInt8] to Pointer[T]
        #does not modify the data, just provide "a viewer"
        return tmp.bitcast[T]()

    #when instance of file_reader is deleted
    fn __del__(owned self):
        #free memory
        self.data.free()
# ruby code to generate data
File.write("name.extension",[1,2,3,4,1.1,2.5].pack("l<4g2"))