Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 14 additions & 17 deletions src/Fabulous/Array.fs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ module ArraySlice =
module Array =
let inline appendOne (v: 'v) (arr: 'v array) =
let res = Array.zeroCreate(arr.Length + 1)
res[.. arr.Length - 1] <- arr
arr.CopyTo(res.AsSpan())
res[arr.Length] <- v
res

Expand Down Expand Up @@ -89,7 +89,7 @@ module StackAllocatedCollections =
[<NoComparison; NoEquality>]
type private Part<'v> =
| Empty
| Filled of struct (Items<'v> * Part<'v>)
| Filled of Items<'v> * Part<'v>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removes indirection.


[<Struct; NoComparison; NoEquality>]
type StackList<'v> =
Expand Down Expand Up @@ -258,8 +258,8 @@ module StackAllocatedCollections =

[<Struct; NoComparison; NoEquality>]
type StackArray3<'v> =
| Few of data: struct (Size * 'v * 'v * 'v)
| Many of arr: 'v array
| Few of data: Size * 'v * 'v * 'v
Comment on lines -261 to +262
Copy link
Contributor Author

@kerams kerams May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removes indirection and decreases total DU struct size with some type parameters (48 to 40 bytes for string).


module StackArray3 =

Expand All @@ -278,7 +278,7 @@ module StackAllocatedCollections =

let add (arr: StackArray3<'v> inref, v: 'v) : StackArray3<'v> =
match arr with
| Few(struct (size, v0, v1, v2)) ->
| Few(size, v0, v1, v2) ->
match size with
| Size.Zero -> one v
| Size.One -> two(v0, v)
Expand All @@ -290,13 +290,13 @@ module StackAllocatedCollections =

let inline length (arr: StackArray3<'v> inref) : int =
match arr with
| Few(struct (size, _, _, _)) -> int size
| Few(size, _, _, _) -> int size
| Many arr -> arr.Length


let get (arr: StackArray3<'v> inref) (index: int) : 'v =
match arr with
| Few(struct (size, v0, v1, v2)) ->
| Few(size, v0, v1, v2) ->
if (index >= int size) then
IndexOutOfRangeException() |> raise
else
Expand All @@ -310,7 +310,7 @@ module StackAllocatedCollections =

let find (test: 'v -> bool) (arr: StackArray3<'v> inref) : 'v =
match arr with
| Few(struct (size, v0, v1, v2)) ->
| Few(size, v0, v1, v2) ->
match (size, test v0, test v1, test v2) with
| Size.One, true, _, _
| Size.Two, true, _, _
Expand All @@ -327,7 +327,7 @@ module StackAllocatedCollections =
/// In Many case it sorts the Many variant inline for optimization reasons
let rec inline sortInPlace<'T, 'V when 'V: comparison> ([<InlineIfLambda>] getKey: 'T -> 'V) (arr: StackArray3<'T> inref) : StackArray3<'T> =
match arr with
| Few(struct (size, v0, v1, v2)) ->
| Few(size, v0, v1, v2) ->
match size with
| Size.Zero
| Size.One -> arr
Expand Down Expand Up @@ -358,15 +358,13 @@ module StackAllocatedCollections =
| _ -> empty() // should never happen but don't want to throw there
| Many arr -> many(Array.sortInPlace getKey arr)


let inline private arr0 () = [||]
let inline private arr1 (v: 'v) = [| v |]
let inline private arr2 (v0: 'v, v1: 'v) = [| v0; v1 |]
let inline private arr3 (v0: 'v, v1: 'v, v2: 'v) = [| v0; v1; v2 |]

let toArray (arr: StackArray3<'v> inref) : 'v array =
match arr with
| Few(struct (size, v0, v1, v2)) ->
| Few(size, v0, v1, v2) ->
match size with
| Size.Zero -> Array.empty
| Size.One -> arr1 v0
Expand All @@ -377,7 +375,7 @@ module StackAllocatedCollections =

let combine (a: StackArray3<'v>) (b: StackArray3<'v>) : StackArray3<'v> =
match (a, b) with
| Few(struct (asize, a0, a1, a2)), Few(struct (bsize, b0, b1, b2)) ->
| Few(asize, a0, a1, a2), Few(bsize, b0, b1, b2) ->
match (asize, bsize) with
| Size.Zero, _ -> b
| _, Size.Zero -> a
Expand Down Expand Up @@ -609,10 +607,9 @@ module StackAllocatedCollections =

[<Struct>]
type Op =
| Added of added: uint16
| Removed of removed: uint16
| Changed of changed: uint16

| Added of value: uint16
| Removed of value: uint16
| Changed of value: uint16
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merges fields and reduces struct size from 12 to 8 bytes.


let inline create () = DiffBuilder(stackalloc<uint16> 8, 0)

Expand Down Expand Up @@ -650,7 +647,7 @@ module StackAllocatedCollections =
| OpCode.ChangeCode -> Changed(value)
| _ -> IndexOutOfRangeException() |> raise

let inline toArray (builder: DiffBuilder byref) (map: Op -> 't) : 't array =
let inline toArray (builder: DiffBuilder byref) ([<InlineIfLambda>] map: Op -> 't) : 't array =
let len = lenght &builder
let res = Array.zeroCreate<'t> len

Expand Down
5 changes: 3 additions & 2 deletions src/Fabulous/View.fs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ module View =
WidgetBuilder<'msg, Memo.Memoized<'marker>>(Memo.MemoWidgetKey, Memo.MemoAttribute.WithValue(memo))

/// Map the widget's message type to the parent's message type to allow for view composition
let inline map (fn: 'oldMsg -> 'newMsg) (x: WidgetBuilder<'oldMsg, 'marker>) : WidgetBuilder<'newMsg, 'marker> =
let inline map ([<InlineIfLambda>] fn: 'oldMsg -> 'newMsg) (x: WidgetBuilder<'oldMsg, 'marker>) : WidgetBuilder<'newMsg, 'marker> =
let replaceWith (oldAttr: ScalarAttribute) =
let fnWithBoxing (msg: obj) =
let oldFn = unbox<obj -> obj> oldAttr.Value
Expand All @@ -56,4 +56,5 @@ module View =
WidgetBuilder<'newMsg, 'marker>(builder.Key, builder.Attributes)

/// Combine map and lazy. Map the widget's message type to the parent's message type, and then memoize it
let inline lazyMap (mapFn: 'oldMsg -> 'newMsg) (viewFn: 'key -> WidgetBuilder<'oldMsg, 'marker>) (model: 'key) = lazy' (viewFn >> map mapFn) model
let inline lazyMap ([<InlineIfLambda>] mapFn: 'oldMsg -> 'newMsg) ([<InlineIfLambda>] viewFn: 'key -> WidgetBuilder<'oldMsg, 'marker>) (model: 'key) =
lazy' (viewFn >> map mapFn) model