refs #35 Buffer is not mutable, MutableBuffer added (to cache in serialized form)
This commit is contained in:
		
							parent
							
								
									f3d766d1b1
								
							
						
					
					
						commit
						7aee25ffef
					
				@ -1,7 +1,8 @@
 | 
				
			|||||||
# Binary `Buffer`
 | 
					# Binary `Buffer`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Buffers are effective unsigned byte arrays of fixed size. Buffers content is mutable,
 | 
					Buffers are effective unsigned byte arrays of fixed size. Buffers content is mutable,
 | 
				
			||||||
unlike its size. Buffers are comparable and implement [Array], thus [Collection] and [Iterable]. Buffer iterators return its contents as unsigned bytes converted to `Int`
 | 
					unlike its size. Buffers are comparable and implement [Array], thus [Collection] and [Iterable]. Buffer iterators return
 | 
				
			||||||
 | 
					its contents as unsigned bytes converted to `Int`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Buffers needs to be imported with `import lyng.buffer`:
 | 
					Buffers needs to be imported with `import lyng.buffer`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -10,6 +11,8 @@ Buffers needs to be imported with `import lyng.buffer`:
 | 
				
			|||||||
    assertEquals(5, Buffer("Hello").size)
 | 
					    assertEquals(5, Buffer("Hello").size)
 | 
				
			||||||
    >>> void
 | 
					    >>> void
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Buffer is _immutable_, there is a `MutableBuffer` with same interface but mutable.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Constructing
 | 
					## Constructing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
There are a lo of ways to construct a buffer:
 | 
					There are a lo of ways to construct a buffer:
 | 
				
			||||||
@ -36,15 +39,23 @@ There are a lo of ways to construct a buffer:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    >>> void
 | 
					    >>> void
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Accessing an modifying
 | 
					## Accessing and modifying
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Buffer implement [Array] and therefore can be accessed and modified with indexing:
 | 
					Buffer implement [Array] and therefore can be accessed, and `MutableBuffers` also modified:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    import lyng.buffer
 | 
					    import lyng.buffer
 | 
				
			||||||
    val b1 = Buffer( 1, 2, 3)
 | 
					    val b1 = Buffer( 1, 2, 3)
 | 
				
			||||||
    assertEquals( 2, b1[1] )
 | 
					    assertEquals( 2, b1[1] )
 | 
				
			||||||
    b1[0] = 199
 | 
					
 | 
				
			||||||
    assertEquals(199, b1[0])
 | 
					    val b2 = b1.toMutable()
 | 
				
			||||||
 | 
					    assertEquals( 2, b1[1] )
 | 
				
			||||||
 | 
					    b2[1]++
 | 
				
			||||||
 | 
					    b2[0] = 100
 | 
				
			||||||
 | 
					    assertEquals( Buffer(100, 3, 3), b2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // b2 is a mutable copy so b1 has not been changed:
 | 
				
			||||||
 | 
					    assertEquals( Buffer(1, 2, 3), b1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    >>> void
 | 
					    >>> void
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Buffer provides concatenation with another Buffer:
 | 
					Buffer provides concatenation with another Buffer:
 | 
				
			||||||
@ -58,12 +69,12 @@ Please note that indexed bytes are _readonly projection_, e.g. you can't modify
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Comparing
 | 
					## Comparing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Buffers are comparable with other buffers:
 | 
					Buffers are comparable with other buffers (and notice there are _mutable_ buffers, bu default buffers ar _immutable_):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    import lyng.buffer
 | 
					    import lyng.buffer
 | 
				
			||||||
    val b1 = Buffer(1, 2, 3)
 | 
					    val b1 = Buffer(1, 2, 3)
 | 
				
			||||||
    val b2 = Buffer(1, 2, 3)
 | 
					    val b2 = Buffer(1, 2, 3)
 | 
				
			||||||
    val b3 = Buffer(b2)
 | 
					    val b3 = MutableBuffer(b2)
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    b3[0] = 101
 | 
					    b3[0] = 101
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -93,20 +104,18 @@ As with [List], it is possible to use ranges as indexes to slice a Buffer:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Members
 | 
					## Members
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| name                | meaning                              | type  |
 | 
					| name          | meaning                            | type          |
 | 
				
			||||||
|---------------------|--------------------------------------|-------|
 | 
					|---------------|------------------------------------|---------------|
 | 
				
			||||||
| `size`              | size                         | Int   |
 | 
					| `size`        | size                               | Int           |
 | 
				
			||||||
| `+=`                | add one or more elements             | Any   |
 | 
					| `decodeUtf8`  | decodee to String using UTF8 rules | Any           |
 | 
				
			||||||
| `+`, `union`         | union sets                           | Any   |
 | 
					| `+`           | buffer concatenation               | Any           |
 | 
				
			||||||
| `-`, `subtract`     | subtract sets                        | Any   |
 | 
					| `toMutable()` | create a mutable copy              | MutableBuffer |
 | 
				
			||||||
| `*`, `intersect`    | subtract sets                        | Any   |
 | 
					 | 
				
			||||||
| `remove(items...)`  | remove one or more items             | Range |
 | 
					 | 
				
			||||||
| `contains(element)` | check the element is in the list (1) |       |
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
(1)
 | 
					(1)
 | 
				
			||||||
: optimized implementation that override `Iterable` one
 | 
					: optimized implementation that override `Iterable` one
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Also, it inherits methods from [Iterable].
 | 
					Also, it inherits methods from [Iterable] and [Array].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Range]: Range.md
 | 
					[Range]: Range.md
 | 
				
			||||||
 | 
					[Iterable]: Iterable.md
 | 
				
			||||||
@ -82,6 +82,11 @@ open class Scope(
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun requireNoArgs() {
 | 
				
			||||||
 | 
					        if( args.list.isNotEmpty())
 | 
				
			||||||
 | 
					            raiseError("This function does not accept any arguments")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    inline fun <reified T : Obj> thisAs(): T = (thisObj as? T)
 | 
					    inline fun <reified T : Obj> thisAs(): T = (thisObj as? T)
 | 
				
			||||||
        ?: raiseClassCastError("Cannot cast ${thisObj.objClass.className} to ${T::class.simpleName}")
 | 
					        ?: raiseClassCastError("Cannot cast ${thisObj.objClass.className} to ${T::class.simpleName}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -179,6 +179,7 @@ class Script(
 | 
				
			|||||||
            ImportManager(rootScope, SecurityManager.allowAll).apply {
 | 
					            ImportManager(rootScope, SecurityManager.allowAll).apply {
 | 
				
			||||||
                addPackage("lyng.buffer") {
 | 
					                addPackage("lyng.buffer") {
 | 
				
			||||||
                    it.addConst("Buffer", ObjBuffer.type)
 | 
					                    it.addConst("Buffer", ObjBuffer.type)
 | 
				
			||||||
 | 
					                    it.addConst("MutableBuffer", ObjMutableBuffer.type)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                addPackage("lyng.time") {
 | 
					                addPackage("lyng.time") {
 | 
				
			||||||
                    it.addConst("Instant", ObjInstant.type)
 | 
					                    it.addConst("Instant", ObjInstant.type)
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ import net.sergeych.lyng.Scope
 | 
				
			|||||||
import net.sergeych.lyng.statement
 | 
					import net.sergeych.lyng.statement
 | 
				
			||||||
import kotlin.math.min
 | 
					import kotlin.math.min
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ObjBuffer(val byteArray: UByteArray) : Obj() {
 | 
					open class ObjBuffer(val byteArray: UByteArray) : Obj() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override val objClass: ObjClass = type
 | 
					    override val objClass: ObjClass = type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -30,16 +30,6 @@ class ObjBuffer(val byteArray: UByteArray) : Obj() {
 | 
				
			|||||||
        } else ObjInt(byteArray[checkIndex(scope, index)].toLong(), true)
 | 
					        } else ObjInt(byteArray[checkIndex(scope, index)].toLong(), true)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun putAt(scope: Scope, index: Obj, newValue: Obj) {
 | 
					 | 
				
			||||||
        byteArray[checkIndex(scope, index.toObj())] = when (newValue) {
 | 
					 | 
				
			||||||
            is ObjInt -> newValue.value.toUByte()
 | 
					 | 
				
			||||||
            is ObjChar -> newValue.value.code.toUByte()
 | 
					 | 
				
			||||||
            else -> scope.raiseIllegalArgument(
 | 
					 | 
				
			||||||
                "invalid byte value for buffer at index ${index.inspect()}: ${newValue.inspect()}"
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val size by byteArray::size
 | 
					    val size by byteArray::size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun hashCode(): Int {
 | 
					    override fun hashCode(): Int {
 | 
				
			||||||
@ -144,18 +134,10 @@ class ObjBuffer(val byteArray: UByteArray) : Obj() {
 | 
				
			|||||||
                    thisAs<ObjBuffer>().byteArray.toByteArray().decodeToString()
 | 
					                    thisAs<ObjBuffer>().byteArray.toByteArray().decodeToString()
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
//            )
 | 
					            addFn("toMutable") {
 | 
				
			||||||
//            addFn("getAt") {
 | 
					                requireNoArgs()
 | 
				
			||||||
//                requireExactCount(1)
 | 
					                ObjMutableBuffer(thisAs<ObjBuffer>().byteArray.copyOf())
 | 
				
			||||||
//                thisAs<ObjList>().getAt(this, requiredArg<Obj>(0))
 | 
					            }
 | 
				
			||||||
//            }
 | 
					 | 
				
			||||||
//            addFn("putAt") {
 | 
					 | 
				
			||||||
//                requireExactCount(2)
 | 
					 | 
				
			||||||
//                val newValue = args[1]
 | 
					 | 
				
			||||||
//                thisAs<ObjList>().putAt(this, requiredArg<ObjInt>(0).value.toInt(), newValue)
 | 
					 | 
				
			||||||
//                newValue
 | 
					 | 
				
			||||||
//            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					package net.sergeych.lyng.obj
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.map
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.toList
 | 
				
			||||||
 | 
					import net.sergeych.lyng.Scope
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ObjMutableBuffer(byteArray: UByteArray) : ObjBuffer(byteArray) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override suspend fun putAt(scope: Scope, index: Obj, newValue: Obj) {
 | 
				
			||||||
 | 
					        byteArray[checkIndex(scope, index.toObj())] = when (newValue) {
 | 
				
			||||||
 | 
					            is ObjInt -> newValue.value.toUByte()
 | 
				
			||||||
 | 
					            is ObjChar -> newValue.value.code.toUByte()
 | 
				
			||||||
 | 
					            else -> scope.raiseIllegalArgument(
 | 
				
			||||||
 | 
					                "invalid byte value for buffer at index ${index.inspect()}: ${newValue.inspect()}"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    companion object {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private suspend fun createBufferFrom(scope: Scope, obj: Obj): ObjBuffer =
 | 
				
			||||||
 | 
					            when (obj) {
 | 
				
			||||||
 | 
					                is ObjBuffer -> ObjMutableBuffer(obj.byteArray.copyOf())
 | 
				
			||||||
 | 
					                is ObjInt -> {
 | 
				
			||||||
 | 
					                    if (obj.value < 0)
 | 
				
			||||||
 | 
					                        scope.raiseIllegalArgument("buffer size must be positive")
 | 
				
			||||||
 | 
					                    val data = UByteArray(obj.value.toInt())
 | 
				
			||||||
 | 
					                    ObjMutableBuffer(data)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                is ObjString -> ObjMutableBuffer(obj.value.encodeToByteArray().asUByteArray())
 | 
				
			||||||
 | 
					                else -> {
 | 
				
			||||||
 | 
					                    if (obj.isInstanceOf(ObjIterable)) {
 | 
				
			||||||
 | 
					                        ObjMutableBuffer(
 | 
				
			||||||
 | 
					                            obj.toFlow(scope).map { it.toLong().toUByte() }.toList().toTypedArray()
 | 
				
			||||||
 | 
					                                .toUByteArray()
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    } else
 | 
				
			||||||
 | 
					                        scope.raiseIllegalArgument(
 | 
				
			||||||
 | 
					                            "can't construct buffer from ${obj.inspect()}"
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val type = object : ObjClass("MutableBuffer", ObjBuffer.type) {
 | 
				
			||||||
 | 
					            override suspend fun callOn(scope: Scope): Obj {
 | 
				
			||||||
 | 
					                val args = scope.args.list
 | 
				
			||||||
 | 
					                return when (args.size) {
 | 
				
			||||||
 | 
					                    // empty buffer
 | 
				
			||||||
 | 
					                    0 -> ObjMutableBuffer(ubyteArrayOf())
 | 
				
			||||||
 | 
					                    1 -> createBufferFrom(scope, args[0])
 | 
				
			||||||
 | 
					                    else -> {
 | 
				
			||||||
 | 
					                        // create buffer from array, each argument should be a byte then:
 | 
				
			||||||
 | 
					                        val data = UByteArray(args.size)
 | 
				
			||||||
 | 
					                        for ((i, b) in args.withIndex()) {
 | 
				
			||||||
 | 
					                            val code = when (b) {
 | 
				
			||||||
 | 
					                                is ObjChar -> b.value.code.toUByte()
 | 
				
			||||||
 | 
					                                is ObjInt -> b.value.toUByte()
 | 
				
			||||||
 | 
					                                else -> scope.raiseIllegalArgument(
 | 
				
			||||||
 | 
					                                    "invalid byte value for buffer constructor at index $i: ${b.inspect()}"
 | 
				
			||||||
 | 
					                                )
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            data[i] = code
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        ObjMutableBuffer(data)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -2477,12 +2477,14 @@ class ScriptTest {
 | 
				
			|||||||
            assertEquals( 3, Buffer(1, 2, 3).size )
 | 
					            assertEquals( 3, Buffer(1, 2, 3).size )
 | 
				
			||||||
            assertEquals( 5, Buffer("hello").size )
 | 
					            assertEquals( 5, Buffer("hello").size )
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            val buffer = Buffer("Hello")
 | 
					            var buffer = Buffer("Hello")
 | 
				
			||||||
            assertEquals( 5, buffer.size)
 | 
					            assertEquals( 5, buffer.size)
 | 
				
			||||||
            assertEquals('l'.code, buffer[2] )
 | 
					            assertEquals('l'.code, buffer[2] )
 | 
				
			||||||
            assertEquals('l'.code, buffer[3] )
 | 
					            assertEquals('l'.code, buffer[3] )
 | 
				
			||||||
            assertEquals("Hello", buffer.decodeUtf8())
 | 
					            assertEquals("Hello", buffer.decodeUtf8())
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
 | 
					            buffer = buffer.toMutable()
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            buffer[2] = 101
 | 
					            buffer[2] = 101
 | 
				
			||||||
            assertEquals(101, buffer[2])
 | 
					            assertEquals(101, buffer[2])
 | 
				
			||||||
            assertEquals("Heelo", buffer.decodeUtf8())
 | 
					            assertEquals("Heelo", buffer.decodeUtf8())
 | 
				
			||||||
@ -2503,6 +2505,12 @@ class ScriptTest {
 | 
				
			|||||||
            val b3 = b1 + Buffer("!")
 | 
					            val b3 = b1 + Buffer("!")
 | 
				
			||||||
            assertEquals( "Hello!", b3.decodeUtf8())
 | 
					            assertEquals( "Hello!", b3.decodeUtf8())
 | 
				
			||||||
            assert( b3 > b1 )
 | 
					            assert( b3 > b1 )
 | 
				
			||||||
 | 
					            assert( b1 !== b2)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            val map = Map( b1 => "foo")
 | 
				
			||||||
 | 
					            assertEquals("foo",  map[b1])
 | 
				
			||||||
 | 
					            assertEquals("foo",  map[b2])
 | 
				
			||||||
 | 
					            assertEquals(null,  map[b3])
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
        """.trimIndent())
 | 
					        """.trimIndent())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -2615,7 +2623,7 @@ class ScriptTest {
 | 
				
			|||||||
        
 | 
					        
 | 
				
			||||||
        import lyng.buffer
 | 
					        import lyng.buffer
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        val b = Buffer(1,2,3)
 | 
					        val b = MutableBuffer(1,2,3)
 | 
				
			||||||
        b[1]++
 | 
					        b[1]++
 | 
				
			||||||
        assert( b == Buffer(1,3,3) )
 | 
					        assert( b == Buffer(1,3,3) )
 | 
				
			||||||
        ++b[0]
 | 
					        ++b[0]
 | 
				
			||||||
@ -2633,7 +2641,7 @@ class ScriptTest {
 | 
				
			|||||||
        
 | 
					        
 | 
				
			||||||
        import lyng.buffer
 | 
					        import lyng.buffer
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        val b = Buffer(1,2,3)
 | 
					        val b = Buffer(1,2,3).toMutable()
 | 
				
			||||||
        b[1]--
 | 
					        b[1]--
 | 
				
			||||||
        assert( b == Buffer(1,1,3) )
 | 
					        assert( b == Buffer(1,1,3) )
 | 
				
			||||||
        --b[0]
 | 
					        --b[0]
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user