Files
advent-of-code-2023/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/EfficientString.fs
patrick 3b98d704d1
All checks were successful
ci/woodpecker/push/build Pipeline was successful
ci/woodpecker/push/all-checks-complete Pipeline was successful
Day 9 (#10)
Went with Lagrange interpolation instead because that seemed easier.

Co-authored-by: Smaug123 <patrick+github@patrickstevens.co.uk>
Reviewed-on: #10
2023-12-09 09:46:17 +00:00

109 lines
3.2 KiB
Forth

namespace AdventOfCode2023
open System
open System.Runtime.CompilerServices
type EfficientString = System.ReadOnlySpan<char>
[<RequireQualifiedAccess>]
module EfficientString =
let inline isEmpty (s : EfficientString) : bool = s.IsEmpty
let inline ofString (s : string) : EfficientString = s.AsSpan ()
let inline toString (s : EfficientString) : string = s.ToString ()
let inline trimStart (s : EfficientString) : EfficientString = s.TrimStart ()
let inline slice (start : int) (length : int) (s : EfficientString) : EfficientString = s.Slice (start, length)
let inline equals (a : string) (other : EfficientString) : bool =
MemoryExtensions.Equals (other, a.AsSpan (), StringComparison.Ordinal)
/// Mutates the input to drop up to the first instance of the input char,
/// and returns what was dropped.
/// If the char is not present, deletes the input.
let takeUntil<'a> (c : char) (s : EfficientString byref) : EfficientString =
let first = s.IndexOf c
if first < 0 then
let toRet = s
s <- EfficientString.Empty
toRet
else
let toRet = slice 0 first s
s <- slice (first + 1) (s.Length - first - 1) s
toRet
[<Struct>]
[<IsByRefLike>]
type StringSplitEnumerator =
internal
{
Original : EfficientString
mutable Remaining : EfficientString
mutable InternalCurrent : EfficientString
SplitOn : char
}
interface IDisposable with
member this.Dispose () = ()
member this.Current : EfficientString = this.InternalCurrent
member this.MoveNext () =
if this.Remaining.Length = 0 then
false
else
this.InternalCurrent <- EfficientString.takeUntil this.SplitOn &this.Remaining
true
member this.GetEnumerator () = this
[<RequireQualifiedAccess>]
module StringSplitEnumerator =
let make (splitChar : char) (s : string) : StringSplitEnumerator =
{
Original = EfficientString.ofString s
Remaining = EfficientString.ofString s
InternalCurrent = EfficientString.Empty
SplitOn = splitChar
}
let make' (splitChar : char) (s : ReadOnlySpan<char>) : StringSplitEnumerator =
{
Original = s
Remaining = s
InternalCurrent = EfficientString.Empty
SplitOn = splitChar
}
let chomp (s : string) (e : byref<StringSplitEnumerator>) : unit =
#if DEBUG
if not (e.MoveNext ()) || not (EfficientString.equals s e.Current) then
failwithf "expected '%s', got '%s'" s (e.Current.ToString ())
#else
e.MoveNext () |> ignore
#endif
let consumeInt (e : byref<StringSplitEnumerator>) : int =
if not (e.MoveNext ()) then
failwith "expected an int, got nothing"
Int32.Parse e.Current
let consumeU32 (e : byref<StringSplitEnumerator>) : uint32 =
if not (e.MoveNext ()) then
failwith "expected an int, got nothing"
UInt32.Parse e.Current
let consumeU64 (e : byref<StringSplitEnumerator>) : uint64 =
if not (e.MoveNext ()) then
failwith "expected an int, got nothing"
UInt64.Parse e.Current