packages feed

hermes-json 0.8.0.0 → 0.8.1.0

raw patch · 9 files changed

+115/−2 lines, 9 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Data.Hermes: objectFold :: state -> (Text -> state -> Decoder state) -> Decoder state
+ Data.Hermes: withRawJsonByteString :: (ByteString -> Decoder a) -> Decoder a
+ Data.Hermes.Decoder.Value: objectFold :: state -> (Text -> state -> Decoder state) -> Decoder state
+ Data.Hermes.Decoder.Value: withRawJsonByteString :: (ByteString -> Decoder a) -> Decoder a
+ Data.Hermes.SIMDJSON.Bindings: getRawJSONImpl :: Value -> Ptr CString -> Ptr CSize -> IO CInt

Files

CHANGELOG.md view
@@ -1,5 +1,16 @@ # Revision history for hermes-json +## 0.8.1.0 -- 2026-08-30++### Changed+* Update simdjson to 4.6.9++### Added+* Add `objectFold` for folding over object fields with a dependent state -+  thanks to @mpscholten!+* Add `withRawJsonByteString` for access to the raw JSON of the current value -+  thanks to @mpscholten!+ ## 0.8.0.0 -- 2026-03-25  Maintenance release
cbits/lib.cpp view
@@ -202,6 +202,20 @@     len = buf.length();   } +  error_code get_raw_json(+      ondemand::value &val,+      const char **out,+      size_t &len) {+    std::string_view buf;+    auto error = val.raw_json().get(buf);+    if (error) {+      return error;+    }+    *out = buf.data();+    len = buf.length();+    return SUCCESS;+  }+   error_code is_null(ondemand::value &val, bool &out) {     return val.is_null().get(out);   }
hermes-json.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               hermes-json-version:            0.8.0.0+version:            0.8.1.0 category:           Text, Web, JSON, FFI synopsis:           Fast JSON decoding via simdjson C++ bindings description:
simdjson/singleheader/simdjson.cpp view

file too large to diff

simdjson/singleheader/simdjson.h view

file too large to diff

src/Data/Hermes.hs view
@@ -51,6 +51,7 @@   , vector   , nullable   , objectAsKeyValues+  , objectFold   , objectAsMap   , objectAsMapExcluding   , liftObjectDecoder@@ -84,6 +85,7 @@   , withVector   -- * Raw ByteString access   , withRawByteString+  , withRawJsonByteString     -- * simdjson Opaque Types   , Array   , ArrayIter
src/Data/Hermes/Decoder/Value.hs view
@@ -18,6 +18,7 @@   , nullable   , object   , objectAsKeyValues+  , objectFold   , objectAsMap   , objectAsMapExcluding   , parseScientific@@ -33,6 +34,7 @@   , withInt   , withObjectAsMap   , withRawByteString+  , withRawJsonByteString   , withRawText   , withScientific   , withString@@ -115,6 +117,16 @@ withRawByteString f = Decoder $ \val -> getRawByteString val >>= \b -> runDecoder (f b) val {-# INLINE withRawByteString #-} +-- | Helper to work with the complete raw JSON representation of the current+-- value. Unlike 'withRawByteString', this consumes and returns a complete+-- nested array or object rather than only its opening token.+withRawJsonByteString :: (BS.ByteString -> Decoder a) -> Decoder a+withRawJsonByteString f =+  Decoder $ \val ->+    getRawJsonByteString val >>= \bytes ->+      runDecoder (f bytes) val+{-# INLINE withRawJsonByteString #-}+ -- | Helper to work with the raw ByteString of the JSON token parsed from the given Value. withRawText :: (Text -> Decoder a) -> Decoder a withRawText f = Decoder $ \val -> getRawText val >>= \b -> runDecoder (f b) val@@ -208,6 +220,17 @@ objectAsKeyValues kf vf = withObjectIter $ iterateOverFields kf vf {-# INLINE objectAsKeyValues #-} +-- | Fold over an object once, selecting the value decoder from the current+-- key and accumulator. This supports dependent object codecs without+-- materialising a key/value list or rescanning the object.+objectFold+  :: state+  -> (Text -> state -> Decoder state)+  -> Decoder state+objectFold initial step =+  withObjectIter $ iterateOverFieldsFold initial step+{-# INLINE objectFold #-}+ -- | Parse an object into a strict `Map`. objectAsMap   :: Ord k@@ -516,6 +539,32 @@           pure $ DList.toList acc {-# INLINE iterateOverFields #-} +iterateOverFieldsFold+  :: state+  -> (Text -> state -> Decoder state)+  -> ObjectIter+  -> DecoderM state+iterateOverFieldsFold initial step iterPtr =+  withRunInIO $ \run ->+    F.alloca $ \lenPtr ->+      F.alloca $ \keyPtr ->+        allocaValue $ \valPtr -> run $ go initial keyPtr lenPtr valPtr+  where+    go !acc keyPtr lenPtr valPtr = do+      isOver <- fmap F.toBool . liftIO $ objectIterIsDoneImpl iterPtr+      if not isOver+        then do+          err <- liftIO $ objectIterGetCurrentImpl iterPtr keyPtr lenPtr valPtr+          handleErrorCode "" err+          keyLength <- fmap fromIntegral . liftIO $ F.peek lenPtr+          keyCString <- liftIO $ F.peek keyPtr+          key <- parseTextFromCStrLen (keyCString, keyLength)+          next <- withKey key $ runDecoder (step key acc) valPtr+          liftIO $ objectIterMoveNextImpl iterPtr+          go next keyPtr lenPtr valPtr+        else pure acc+{-# INLINE iterateOverFieldsFold #-}+ withUnorderedField :: Value -> Decoder a -> Object -> Text -> DecoderM a withUnorderedField vPtr f objPtr key =   withRunInIO $ \run ->@@ -618,6 +667,20 @@         str <- F.peek strPtr         Unsafe.unsafePackCStringLen (str, len) {-# INLINE getRawByteString #-}++getRawJsonByteString :: Value -> DecoderM BS.ByteString+getRawJsonByteString valPtr = do+  (errorCode, cString, byteLength) <-+    liftIO $+      F.alloca $ \stringPtr ->+        F.alloca $ \lengthPtr -> do+          errorCode <- getRawJSONImpl valPtr stringPtr lengthPtr+          byteLength <- fmap fromIntegral $ F.peek lengthPtr+          cString <- F.peek stringPtr+          pure (errorCode, cString, byteLength)+  handleErrorCode "raw JSON" errorCode+  liftIO $ Unsafe.unsafePackCStringLen (cString, byteLength)+{-# INLINE getRawJsonByteString #-}  getRawText :: Value -> DecoderM Text getRawText valPtr =
src/Data/Hermes/SIMDJSON/Bindings.hs view
@@ -23,6 +23,7 @@   , getObjectFromValueImpl   , getObjectIterFromValueImpl   , getRawJSONTokenImpl+  , getRawJSONImpl   , getStringImpl   , getTypeImpl   , intArrayImpl@@ -145,6 +146,9 @@  foreign import ccall unsafe "get_raw_json_token" getRawJSONTokenImpl   :: Value -> Ptr CString -> Ptr CSize -> IO ()++foreign import ccall unsafe "get_raw_json" getRawJSONImpl+  :: Value -> Ptr CString -> Ptr CSize -> IO CInt  foreign import ccall unsafe "get_type" getTypeImpl   :: Value -> Ptr CInt -> IO CInt
tests/test.hs view
@@ -37,7 +37,26 @@ properties = testGroup "Properties" [rtProp, rtPropOptional, rtErrors, rtRecursiveDataType]  units :: TestTree-units = testGroup "Units" [altCases, objectFields]+units = testGroup "Units" [altCases, objectFields, dependentObjectFold, rawJsonAccess]++dependentObjectFold :: TestTree+dependentObjectFold = testCase "dependent object fold" $+  decodeEither+    (objectFold (0 :: Int) $ \key total ->+      case key of+        "add" -> (+ total) <$> int+        "enabled" -> (\value -> if value then total + 100 else total) <$> bool+        _ -> total <$ withRawJsonByteString (const (pure ())))+    "{\"add\":2,\"ignored\":{\"nested\":[1,2]},\"enabled\":true,\"add\":3}"+    @?= Right 105++rawJsonAccess :: TestTree+rawJsonAccess = testCase "complete raw JSON access" $+  decodeEither+    (object $ atKey "payload" $+      withRawJsonByteString (\bytes -> pure bytes))+    "{\"payload\":{\"nested\":[1,true,null]}}"+    @?= Right "{\"nested\":[1,true,null]}"  rtRecursiveDataType :: TestTree rtRecursiveDataType = testProperty "Round Trip With Recursive Data Type" $