diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright John Ky (c) 2016
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Author name here nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,18 @@
+# hw-json-simple-cursor
+[![master](https://circleci.com/gh/haskell-works/hw-json-simple-cursor/tree/master.svg?style=svg)](https://circleci.com/gh/haskell-works/hw-json-simple-cursor/tree/master)
+
+`hw-json-simple-cursor` is support library for `hw-json`, a succinct JSON parsing library.
+
+It uses succinct data-structures to allow traversal of large JSON strings with minimal memory overhead.
+
+For more information see [`hw-json`](https://github.com/haskell-works/hw-json).
+
+## References
+
+* [Semi-Indexing Semi-Structured Data in Tiny Space](http://www.di.unipi.it/~ottavian/files/semi_index_cikm.pdf)
+* [Succinct Data Structures talk by Edward Kmett](https://www.youtube.com/watch?v=uA0Z7_4J7u8)
+* [Typed Tagless Final Interpreters](http://okmij.org/ftp/tagless-final/course/lecture.pdf)
+
+## Special mentions
+
+* [Sydney Paper Club](http://www.meetup.com/Sydney-Paper-Club/)
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/app/App/Commands.hs b/app/App/Commands.hs
new file mode 100644
--- /dev/null
+++ b/app/App/Commands.hs
@@ -0,0 +1,13 @@
+module App.Commands where
+
+import App.Commands.CreateIndex
+import Data.Semigroup           ((<>))
+import Options.Applicative
+
+commands :: Parser (IO ())
+commands = commandsGeneral
+
+commandsGeneral :: Parser (IO ())
+commandsGeneral = subparser $ mempty
+  <>  commandGroup "Commands:"
+  <>  cmdCreateIndex
diff --git a/app/App/Commands/CreateIndex.hs b/app/App/Commands/CreateIndex.hs
new file mode 100644
--- /dev/null
+++ b/app/App/Commands/CreateIndex.hs
@@ -0,0 +1,70 @@
+{-# LANGUAGE BangPatterns        #-}
+{-# LANGUAGE DataKinds           #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications    #-}
+
+module App.Commands.CreateIndex
+  ( cmdCreateIndex
+  ) where
+
+import Control.Lens
+import Data.Generics.Product.Any
+import Data.Maybe
+import Data.Semigroup            ((<>))
+import Data.Word
+import Foreign
+import Options.Applicative       hiding (columns)
+
+import qualified App.Commands.Types                             as Z
+import qualified Data.ByteString.Internal                       as BSI
+import qualified Data.ByteString.Lazy                           as LBS
+import qualified HaskellWorks.Data.ByteString.Lazy              as LBS
+import qualified HaskellWorks.Data.Json.Simple.Cursor.SemiIndex as SISI
+import qualified System.IO.MMap                                 as IO
+
+{-# ANN module ("HLint: ignore Reduce duplication"  :: String) #-}
+{-# ANN module ("HLint: ignore Redundant do"        :: String) #-}
+
+runCreateIndex :: Z.CreateIndexOptions -> IO ()
+runCreateIndex opts = do
+  let filePath = opts ^. the @"filePath"
+  let outputIbFile = opts ^. the @"outputIbFile" & fromMaybe (filePath <> ".ib.idx")
+  let outputBpFile = opts ^. the @"outputBpFile" & fromMaybe (filePath <> ".bp.idx")
+  (fptr :: ForeignPtr Word8, offset, size) <- IO.mmapFileForeignPtr filePath IO.ReadOnly Nothing
+  let !bs = BSI.fromForeignPtr (castForeignPtr fptr) offset size
+  let SISI.SemiIndex _ ibs bps = SISI.buildSemiIndex bs
+  LBS.writeFile outputIbFile (LBS.toLazyByteString ibs)
+  LBS.writeFile outputBpFile (LBS.toLazyByteString bps)
+
+optsCreateIndex :: Parser Z.CreateIndexOptions
+optsCreateIndex = Z.CreateIndexOptions
+  <$> strOption
+        (   long "input"
+        <>  short 'i'
+        <>  help "Input JSON file"
+        <>  metavar "STRING"
+        )
+  <*> strOption
+        (   long "backend"
+        <>  short 'b'
+        <>  value "standard"
+        <>  help "Backend for creating index"
+        <>  metavar "STRING"
+        )
+  <*> optional
+        ( strOption
+          (   long "output-ib-file"
+          <>  help "Filename for output ib index"
+          <>  metavar "STRING"
+          )
+        )
+  <*> optional
+        ( strOption
+          (   long "output-bp-file"
+          <>  help "Filename for output bp index"
+          <>  metavar "STRING"
+          )
+        )
+
+cmdCreateIndex :: Mod CommandFields (IO ())
+cmdCreateIndex = command "create-index"  $ flip info idm $ runCreateIndex <$> optsCreateIndex
diff --git a/app/App/Commands/Types.hs b/app/App/Commands/Types.hs
new file mode 100644
--- /dev/null
+++ b/app/App/Commands/Types.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE DeriveGeneric         #-}
+{-# LANGUAGE DuplicateRecordFields #-}
+
+module App.Commands.Types
+  ( CreateIndexOptions(..)
+  ) where
+
+import GHC.Generics
+
+data CreateIndexOptions = CreateIndexOptions
+  { filePath     :: FilePath
+  , backend      :: String
+  , outputIbFile :: Maybe FilePath
+  , outputBpFile :: Maybe FilePath
+  } deriving (Eq, Show, Generic)
diff --git a/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,11 @@
+module Main where
+
+import App.Commands
+import Control.Monad
+import Data.Semigroup      ((<>))
+import Options.Applicative
+
+main :: IO ()
+main = join $ customExecParser
+  (prefs $ showHelpOnEmpty <> showHelpOnError)
+  (info (commands <**> helper) idm)
diff --git a/bench/Main.hs b/bench/Main.hs
new file mode 100644
--- /dev/null
+++ b/bench/Main.hs
@@ -0,0 +1,10 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Main where
+
+import Criterion.Main
+
+main :: IO ()
+main = do
+  benchmarks <- fmap mconcat $ sequence $ mempty
+  defaultMain benchmarks
diff --git a/corpus/5000B.json b/corpus/5000B.json
new file mode 100644
--- /dev/null
+++ b/corpus/5000B.json
@@ -0,0 +1,1 @@
+[ { "_id" : { "$oid" : "52cdef7c4bab8bd675297d8a" }, "name" : "Wetpaint", "permalink" : "abc2", "crunchbase_url" : "http://www.crunchbase.com/company/wetpaint", "homepage_url" : "http://wetpaint-inc.com", "blog_url" : "http://digitalquarters.net/", "blog_feed_url" : "http://digitalquarters.net/feed/", "twitter_username" : "BachelrWetpaint", "category_code" : "web", "number_of_employees" : 47, "founded_year" : 2005, "founded_month" : 10, "founded_day" : 17, "deadpooled_year" : 1, "tag_list" : "wiki, seattle, elowitz, media-industry, media-platform, social-distribution-system", "alias_list" : "", "email_address" : "info@wetpaint.com", "phone_number" : "206.859.6300", "description" : "Technology Platform Company", "created_at" : { "$date" : 1180075887000 }, "updated_at" : "Sun Dec 08 07:15:44 UTC 2013", "overview" : "<p>Wetpaint is a technology platform company that uses its proprietary state-of-the-art technology and expertise in social media to build and monetize audiences for digital publishers. Wetpaint's own online property, Wetpaint Entertainment, an entertainment news site that attracts more than 12 million unique visitors monthly and has over 2 million Facebook fans, is a proof point to the company's success in building and engaging audiences. Media companies can license Wetpaint's platform which includes a dynamic playbook tailored to their individual needs and comprehensive training. Founded by Internet pioneer Ben Elowitz, and with offices in New York and Seattle, Wetpaint is backed by Accel Partners, the investors behind Facebook.</p>", "image" : { "available_sizes" : [ [ [ 150, 75 ], "assets/images/resized/0000/3604/3604v14-max-150x150.jpg" ], [ [ 250, 125 ], "assets/images/resized/0000/3604/3604v14-max-250x250.jpg" ], [ [ 450, 225 ], "assets/images/resized/0000/3604/3604v14-max-450x450.jpg" ] ] }, "products" : [ { "name" : "Wikison Wetpaint", "permalink" : "wetpaint-wiki" }, { "name" : "Wetpaint Social Distribution System", "permalink" : "wetpaint-social-distribution-system" } ], "relationships" : [ { "is_past" : false, "title" : "Co-Founder and VP, Social and Audience Development", "person" : { "first_name" : "Michael", "last_name" : "Howell", "permalink" : "michael-howell" } }, { "is_past" : false, "title" : "Co-Founder/CEO/Board of Directors", "person" : { "first_name" : "Ben", "last_name" : "Elowitz", "permalink" : "ben-elowitz" } }, { "is_past" : false, "title" : "COO/Board of Directors", "person" : { "first_name" : "Rob", "last_name" : "Grady", "permalink" : "rob-grady" } }, { "is_past" : false, "title" : "SVP, Strategy and Business Development", "person" : { "first_name" : "Chris", "last_name" : "Kollas", "permalink" : "chris-kollas" } }, { "is_past" : false, "title" : "Board", "person" : { "first_name" : "Theresia", "last_name" : "Ranzetta", "permalink" : "theresia-ranzetta" } }, { "is_past" : false, "title" : "Board Member", "person" : { "first_name" : "Gus", "last_name" : "Tai", "permalink" : "gus-tai" } }, { "is_past" : false, "title" : "Board", "person" : { "first_name" : "Len", "last_name" : "Jordan", "permalink" : "len-jordan" } }, { "is_past" : false, "title" : "Head of Technology and Product", "person" : { "first_name" : "Alex", "last_name" : "Weinstein", "permalink" : "alex-weinstein" } }, { "is_past" : true, "title" : "CFO", "person" : { "first_name" : "Bert", "last_name" : "Hogue", "permalink" : "bert-hogue" } }, { "is_past" : true, "title" : "CFO/ CRO", "person" : { "first_name" : "Brian", "last_name" : "Watkins", "permalink" : "brian-watkins" } }, { "is_past" : true, "title" : "Senior Vice President, Marketing", "person" : { "first_name" : "Rob", "last_name" : "Grady", "permalink" : "rob-grady" } }, { "is_past" : true, "title" : "VP, Technology and Product", "person" : { "first_name" : "Werner", "last_name" : "Koepf", "permalink" : "werner-koepf" } }, { "is_past" : true, "title" : "VP Marketing", "person" : { "first_name" : "Kevin", "last_name" : "Flaherty", "permalink" : "kevin-flaherty" } }, { "is_past" : true, "title" : "VP User Experience", "person" : { "first_name" : "Alex", "last_name" : "Berg", "permalink" : "alex-berg" } }, { "is_past" : true, "title" : "VP Engineering", "person" : { "first_name" : "Steve", "last_name" : "McQuade", "permalink" : "steve-mcquade" } }, { "is_past" : true, "title" : "Executive Editor", "person" : { "first_name" : "Susan", "last_name" : "Mulcahy", "permalink" : "susan-mulcahy" } }, { "is_past" : true, "title" : "VP Business Development", "person" : { "first_name" : "Chris", "last_name" : "Kollas", "permalink" : "chris-kollas" } } ], "competitions" : [ { "competitor" : { "name" : "Wikia", "permalink" : "wikia" } }, { "competitor" : { "name" : "JotSpot", "permalink" : "jotspot" } }, { "competitor" : { "name" : "Socialtext", "permalink" : "socialtext" } }, { "competitor" : { "name" : "Ning by Glam Media", "permalink" : "ning" } }, { "competitor" : { "name" : "Soceeo", "permalink" : "soceeo" } }, { "competitor" : { "n" : "Y", "" : 1234567}}]}]
diff --git a/corpus/5000B.json.bp.idx b/corpus/5000B.json.bp.idx
new file mode 100644
--- /dev/null
+++ b/corpus/5000B.json.bp.idx
@@ -0,0 +1,1 @@
+111011010010101010101010101010101010101010101010101010101010101010101010101010101010110100101010101011011110100100111010010011101001000010111010101001101010100010111010101010110101010101000110101010101101010101010001101010101011010101010100011010101010110101010101000110101010101101010101010001101010101011010101010100011010101010110101010101000110101010101101010101010001101010101011010101010100011010101010110101010101000110101010101101010101010001101010101011010101010100011010101010110101010101000110101010101101010101010001101010101011010101010100011010101010110101010101000110101010101101010101010000101110110101010001101101010100011011010101000110110101010001101101010100011011010101000000
diff --git a/corpus/5000B.json.ib.idx b/corpus/5000B.json.ib.idx
new file mode 100644
--- /dev/null
+++ b/corpus/5000B.json.ib.idx
@@ -0,0 +1,1 @@
+10101000000010100000000100000000000000000000000000000100000000100000000000100000000000001000000010000000000000000001000000000000000000000000000000000000000000000100000000000000001000000000000000000000000001000000000000100000000000000000000000000000010000000000000000010000000000000000000000000000000000010000000000000000000010000000000000000001000000000000000001000000100000000000000000000000100010000000000000000100000100000000000000000100010000000000000001000100000000000000000001001000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000001000100000000000000000100000000000000000000100000000000000001000000000000000100000000000000010000000000000000000000000000001000000000000001010000000001000000000000000010000000000000010000000000000000000000000000000100000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000001010000000000000000000101010100001000001000000000000000000000000000000000000000000000000000000000000101010000100000010000000000000000000000000000000000000000000000000000000000001010100001000000100000000000000000000000000000000000000000000000000000000000000001000000000000101010000000010000000000000000000100000000000001000000000000000000101000000001000000000000000000000000000000000000001000000000000010000000000000000000000000000000000000000001000000000000000001010100000000000100000010000000001000000000000000000000000000000000000000000000000000001000000000010100000000000000100000000001000000000000010000000001000000000000010000000000000000000001010000000000010000001000000000100000000000000000000000000000000000010000000000101000000000000001000000100000000000001000000000010000000000000100000000000000000010100000000000100000010000000001000000000000000000000000010000000000101000000000000001000000100000000000001000000001000000000000010000000000000000101000000000001000000100000000010000000000000000000000000000000000000000010000000000101000000000000001000000001000000000000010000000001000000000000010000000000000000000101000000000001000000100000000010000000010000000000101000000000000001000000000001000000000000010000000000010000000000000100000000000000000000000010100000000000100000010000000001000000000000000100000000001010000000000000010000001000000000000010000001000000000000010000000000000010100000000000100000010000000001000000001000000000010100000000000000100000010000000000000100000000010000000000000100000000000000000101000000000001000000100000000010000000000000000000000000000000001000000000010100000000000000100000001000000000000010000000000001000000000000010000000000000000000001010000000000010000010000000001000000100000000001010000000000000010000000100000000000001000000001000000000000010000000000000000010100000000000100000100000000010000000000010000000000101000000000000001000000001000000000000010000000000100000000000001000000000000000000001010000000000010000010000000001000000000000000000000000000000000001000000000010100000000000000100000010000000000000100000000100000000000001000000000000000010100000000000100000100000000010000000000000000000000000000010000000000101000000000000001000000000100000000000001000000001000000000000010000000000000000000101000000000001000001000000000100000000000000010000000000101000000000000001000000001000000000000010000000000010000000000000100000000000000000000010100000000000100000100000000010000000000000000000001000000000010100000000000000100000001000000000000010000000100000000000001000000000000000010100000000000100000100000000010000000000000000010000000000101000000000000001000000001000000000000010000000000100000000000001000000000000000000001010000000000010000010000000001000000000000000000010000000000101000000000000001000000001000000000000010000000000100000000000001000000000000000000001010000000000010000010000000001000000000000000000000000001000000000010100000000000000100000000100000000000001000000000100000000000001000000000000000000000100000000000000001010100000000000000101000000001000000001000000000000010000000000001010000000000000010100000000100000000001000000000000010000000000000010100000000000000101000000001000000000000010000000000000100000000000000000101000000000000001010000000010000000000000000000001000000000000010000000000010100000000000000101000000001000000000100000000000001000000000000010100000000000000101000001000010000100000
diff --git a/corpus/issue-0001.json b/corpus/issue-0001.json
new file mode 100644
--- /dev/null
+++ b/corpus/issue-0001.json
@@ -0,0 +1,1 @@
+[ { "total_money_raised" : "$39.8M", "funding_rounds" : [], "investments" : [], "acquisition" : { "price_amount" : 30000000, "price_currency_code" : "USD", "term_code" : "cash_and_stock", "source_url" : "http://allthingsd.com/20131216/viggle-tries-to-bulk-up-its-social-tv-business-by-buying-wetpaint/?mod=atdtweet", "source_description" : " Viggle Tries to Bulk Up Its Social TV Business by Buying Wetpaint", "acquired_year" : 2013, "acquired_month" : 12, "acquired_day" : 16, "acquiring_company" : { "name" : "Viggle", "permalink" : "viggle" } }, "acquisitions" : [], "offices" : [ { "description" : "", "address1" : "710 - 2nd Avenue", "address2" : "Suite 1100", "zip_code" : "98104", "city" : "Seattle", "state_code" : "WA", "country_code" : "USA", "latitude" : 47.603122, "longitude" : -122.333253 }, { "description" : "", "address1" : "270 Lafayette Street", "address2" : "Suite 505", "zip_code" : "10012", "city" : "New York", "state_code" : "NY", "country_code" : "USA", "latitude" : 40.7237306, "longitude" : -73.9964312 } ], "milestones" : [ { "id" : 5869, "description" : "Wetpaint named in Lead411's Hottest Seattle Companies list", "stoned_year" : 2010, "stoned_month" : 6, "stoned_day" : 8, "source_url" : "http://www.lead411.com/seattle-companies.html", "source_text" : null, "source_description" : "LEAD411 LAUNCHES \"HOTTEST SEATTLE COMPANIES\" AWARDS", "stoneable_type" : "Company", "stoned_value" : null, "stoned_value_type" : null, "stoned_acquirer" : null, "stoneable" : { "name" : "Wetpaint", "permalink" : "wetpaint" } }, { "id" : 8702, "description" : "Site-Builder Wetpaint Makes One For Itself, Using the Demand Media Playbook", "stoned_year" : 2010, "stoned_month" : 9, "stoned_day" : 6, "source_url" : "http://mediamemo.allthingsd.com/20100906/site-builder-wetpaint-makes-one-for-itself-using-the-demand-media-playbook/", "source_text" : null, "source_description" : "All Things D", "stoneable_type" : "Company", "stoned_value" : null, "stoned_value_type" : null, "stoned_acquirer" : null, "stoneable" : { "name" : "Wetpaint", "permalink" : "wetpaint" } } ], "video_embeds" : [], "screenshots" : [ { "available_sizes" : [ [ [ 150, 86 ], "assets/images/resized/0016/0929/160929v2-max-150x150.png" ], [ [ 250, 143 ], "assets/images/resized/0016/0929/160929v2-max-250x250.png" ], [ [ 450, 258 ], "assets/images/resized/0016/0929/160929v2-max-450x450.png" ] ], "attribution" : null } ], "external_links" : [ { "external_url" : "http://www.geekwire.com/2011/rewind-ben-elowitz-wetpaint-ceo-building-type-media-company", "title" : "GeekWire interview: Rewind - Ben Elowitz, Wetpaint CEO, on building a new type of media company" }, { "external_url" : "http://techcrunch.com/2012/06/17/search-and-social-how-two-will-soon-become-one/", "title" : "Guest post by CEO Ben Elowitz in TechCrunch" }, { "external_url" : "http://allthingsd.com/20120516/what-to-expect-when-facebook-is-expecting-five-predictions-for-facebooks-first-public-year/", "title" : "Guest post by CEO Ben Elowitz in AllThingsD" }, { "external_url" : "http://adage.com/article/digitalnext/facebook-biggest-player-advertising-s-540-billion-world/235708/", "title" : "Guest post by CEO Ben Elowitz in AdAge" }, { "external_url" : "http://www.businessinsider.com/facebook-captures-14-percent-of-our-online-attention-but-only-4-percent-of-ad-spending-online-2012-6", "title" : "Guest post by CEO Ben Elowitz in Business Insider" }, { "external_url" : "http://allfacebook.com/wetpaint-media-data_b75963", "title" : "AllFacebook coverage of Wetpaint" }, { "external_url" : "http://adage.com/article/digital/celeb-site-wetpaint-shows-media-profit-facebook/237828/", "title" : "Profile of Wetpaint in Ad Age" }, { "external_url" : "http://allthingsd.com/20121018/how-to-boost-your-facebook-traffic-tips-and-tricks-from-wetpaint/", "title" : "Interview with Wetpaint CEO Ben Elowitz in All Things D" }, { "external_url" : "http://www.xconomy.com/seattle/2012/10/19/wetpaint-starts-licensing-its-facebook-based-media-distribution-tech/", "title" : "Profile of Wetpaint in Xconomy" } ]}, {}]
diff --git a/corpus/issue-0001.json.bp.idx b/corpus/issue-0001.json.bp.idx
new file mode 100644
--- /dev/null
+++ b/corpus/issue-0001.json.bp.idx
@@ -0,0 +1,1 @@
+111010101010101011010101010101010101010101010101010110101010001010101110101010101010101010101010101010101001101010101010101010101010101010101010001011101010101010101010101010101010101010101010101010101101010100011010101010101010101010101010101010101010101010101011010101000010101011101111010010011101001001110100100010100010111010101001101010100110101010011010101001101010100110101010011010101001101010100110101010000100
diff --git a/corpus/issue-0001.json.ib.idx b/corpus/issue-0001.json.ib.idx
new file mode 100644
--- /dev/null
+++ b/corpus/issue-0001.json.ib.idx
@@ -0,0 +1,1 @@
+1010100000000000000000000001000000000100000000000000000010001000000000000000100010000000000000001010000000000000000100000000010000000000000000000000010000001000000000000010000000000000000010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000010000000000000000010000010000000000000000001000100000000000000001000100000000000000000000010100000000100000000010000000000000100000000000001000000000000000010001000000000001010100000000000000010001000000000000100000000000000000001000000000000100000000000001000000000000100000000100000000100000000001000000000000001000001000000000000000010000001000000000000100000000001000000000000010000000000000010100000000000000010001000000000000100000000000000000000000100000000000010000000000001000000000000100000000100000000100000000000100000000000000100000100000000000000001000000100000000000010000000000010000000000000100000000000000001000000000000001010100000010000010000000000000001000000000000000000000000000000000000000000000000000000000000010000000000000001000001000000000000000010010000000000000010010000000000000010000000000000000000000000000000000000000000000001000000000000000100000100000000000000000000001000000000000000000000000000000000000000000000000000000001000000000000000000100000000001000000000000000010000010000000000000000000001000001000000000000000000010000010000000000000101000000001000000000001000000000000010000000000000001010000001000001000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000001000001000000000000000010010000000000000010010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000001000001000000000000000000000010000000000000001000000000000000000100000000001000000000000000010000010000000000000000000001000001000000000000000000010000010000000000000101000000001000000000001000000000000010000000000000000010000000000000000100010000000000000001010100000000000000000001010101000010000010000000000000000000000000000000000000000000000000000000000000101010000100000010000000000000000000000000000000000000000000000000000000000000101010000100000010000000000000000000000000000000000000000000000000000000000000001000000000000000100000000010000000000000000001010100000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010100000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000100000000000000000000000000000000000000000000000010100000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000100000000000000000000000000000000000000000000000010100000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000001000000000000000000000000000000000000000000010100000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000100000000000000000000000000000000000000000000000000000010100000000000000001000000000000000000000000000000000000000000000000000010000000001000000000000000000000000000000000000010100000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000001000000000000000000000000000000000010100000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000010000000000000000000000000000000000000000000000000000000000001010000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000001000000000000000000000000000000000000001000000000000000000000000000000
diff --git a/corpus/issue-0001.md b/corpus/issue-0001.md
new file mode 100644
--- /dev/null
+++ b/corpus/issue-0001.md
@@ -0,0 +1,9 @@
+In this issue, there is a missing open close at the end of the JSON document.
+
+Erroneous balance parens calculated:
+
+    |<---------------------- last 64-bits ------------------------>|
+    011010101001101010100110101010000100
+    0110101010011010101001101010100000000000000000000000000000000000
+                                     ^
+                                     +---- missing bit
diff --git a/hw-json-simple-cursor.cabal b/hw-json-simple-cursor.cabal
new file mode 100644
--- /dev/null
+++ b/hw-json-simple-cursor.cabal
@@ -0,0 +1,164 @@
+cabal-version:  2.2
+
+name:           hw-json-simple-cursor
+version:        0.1.0.0
+synopsis:       Memory efficient JSON parser
+description:    Memory efficient JSON parser. Please see README.md
+category:       Data
+homepage:       http://github.com/haskell-works/hw-json-simple-cursor#readme
+bug-reports:    https://github.com/haskell-works/hw-json-simple-cursor/issues
+author:         John Ky
+maintainer:     newhoggy@gmail.com
+copyright:      2016 - 2019 John Ky
+license:        BSD-3-Clause
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    README.md
+    corpus/5000B.json
+    corpus/5000B.json.bp.idx
+    corpus/5000B.json.ib.idx
+    corpus/issue-0001.json
+    corpus/issue-0001.json.bp.idx
+    corpus/issue-0001.json.ib.idx
+    corpus/issue-0001.md
+
+source-repository head
+  type: git
+  location: https://github.com/haskell-works/hw-json-simple-cursor
+
+flag bmi2
+  description:  Enable bmi2 instruction set
+  manual:       False
+  default:      False
+
+flag sse42
+  description:  Enable sse4.2 instruction set
+  manual:       False
+  default:      False
+
+common base                     { build-depends: base                     >= 4          && < 5      }
+
+common ansi-wl-pprint           { build-depends: ansi-wl-pprint           >= 0.6.8.2    && < 0.7    }
+common array                    { build-depends: array                    >= 0.5        && < 0.6    }
+common attoparsec               { build-depends: attoparsec               >= 0.13       && < 0.14   }
+common bits-extra               { build-depends: bits-extra               >= 0.0.1.3    && < 0.1    }
+common bytestring               { build-depends: bytestring               >= 0.10.6     && < 0.11   }
+common criterion                { build-depends: criterion                >= 1.4        && < 1.6    }
+common directory                { build-depends: directory                >= 1.3        && < 1.4    }
+common dlist                    { build-depends: dlist                    >= 0.8        && < 0.9    }
+common generic-lens             { build-depends: generic-lens             >= 1.1.0.0    && < 1.2    }
+common hedgehog                 { build-depends: hedgehog                 >= 0.6        && < 1.1    }
+common hspec                    { build-depends: hspec                    >= 2.4        && < 3      }
+common hw-balancedparens        { build-depends: hw-balancedparens        >= 0.3.0.0    && < 0.4    }
+common hw-bits                  { build-depends: hw-bits                  >= 0.7.0.5    && < 0.8    }
+common hw-hspec-hedgehog        { build-depends: hw-hspec-hedgehog        >= 0.1.0.4    && < 0.2    }
+common hw-json-simd             { build-depends: hw-json-simd             >= 0.1.0.2    && < 0.2    }
+common hw-mquery                { build-depends: hw-mquery                >= 0.2.0.0    && < 0.3    }
+common hw-parser                { build-depends: hw-parser                >= 0.1        && < 0.2    }
+common hw-prim                  { build-depends: hw-prim                  >= 0.6.2.28   && < 0.7    }
+common hw-rankselect            { build-depends: hw-rankselect            >= 0.13       && < 0.14   }
+common hw-rankselect-base       { build-depends: hw-rankselect-base       >= 0.3.2.1    && < 0.4    }
+common hw-simd                  { build-depends: hw-simd                  >= 0.1.1.2    && < 0.2    }
+common lens                     { build-depends: lens                     >= 4          && < 5      }
+common mmap                     { build-depends: mmap                     >= 0.5        && < 0.6    }
+common optparse-applicative     { build-depends: optparse-applicative     >= 0.14       && < 0.15   }
+common text                     { build-depends: text                     >= 1.2        && < 1.3    }
+common transformers             { build-depends: transformers             >= 0.4        && < 0.6    }
+common vector                   { build-depends: vector                   >= 0.12       && < 0.13   }
+common word8                    { build-depends: word8                    >= 0.1        && < 0.2    }
+
+common semigroups   { if impl(ghc <  8    ) { build-depends: semigroups     >= 0.16     && < 0.19 } }
+
+common config
+  default-language:   Haskell2010
+  ghc-options:        -Wall -O2 -msse4.2
+  if flag(sse42)
+    ghc-options: -msse4.2
+  if flag(bmi2) && impl(ghc >= 8.4.1)
+    ghc-options: -mbmi2 -msse4.2
+    cpp-options: -DBMI2_ENABLED
+
+library
+  import:   base, config
+          , bytestring
+          , hw-balancedparens
+          , hw-bits
+          , hw-prim
+          , hw-rankselect
+          , hw-rankselect-base
+          , vector
+          , word8
+  hs-source-dirs:       src
+  other-modules:        Paths_hw_json_simple_cursor
+  autogen-modules:      Paths_hw_json_simple_cursor
+  exposed-modules:      HaskellWorks.Data.Json.Simple.Cursor
+                        HaskellWorks.Data.Json.Simple.Cursor.Fast
+                        HaskellWorks.Data.Json.Simple.Cursor.Internal.IbBp
+                        HaskellWorks.Data.Json.Simple.Cursor.Internal.ToIbBp
+                        HaskellWorks.Data.Json.Simple.Cursor.Internal.Word8
+                        HaskellWorks.Data.Json.Simple.Cursor.SemiIndex
+                        HaskellWorks.Data.Json.Simple.Cursor.Snippet
+
+executable hw-json
+  import:   base, config
+          , bytestring
+          , dlist
+          , generic-lens
+          , hw-balancedparens
+          , hw-json-simd
+          , hw-mquery
+          , hw-prim
+          , hw-rankselect
+          , hw-rankselect-base
+          , lens
+          , mmap
+          , optparse-applicative
+          , semigroups
+          , text
+          , vector
+  main-is:            Main.hs
+  hs-source-dirs:     app
+  ghc-options:        -threaded -rtsopts -with-rtsopts=-N
+  build-depends:      hw-json-simple-cursor
+  other-modules:
+      App.Commands
+      App.Commands.CreateIndex
+      App.Commands.Types
+
+test-suite hw-json-test
+  import:   base, config
+          , attoparsec
+          , bytestring
+          , hedgehog
+          , hspec
+          , hw-balancedparens
+          , hw-bits
+          , hw-hspec-hedgehog
+          , hw-prim
+          , hw-rankselect
+          , hw-rankselect-base
+          , transformers
+          , vector
+  type:               exitcode-stdio-1.0
+  main-is:            Spec.hs
+  build-depends:      hw-json-simple-cursor
+  hs-source-dirs:     test
+  ghc-options:        -threaded -rtsopts -with-rtsopts=-N
+  build-tools:        hspec-discover
+  other-modules:
+      HaskellWorks.Data.Json.Simple.CursorSpec
+      Paths_hw_json_simple_cursor
+
+benchmark bench
+  import:   base, config
+          , bytestring
+          , criterion
+          , directory
+          , mmap
+          , semigroups
+  type:               exitcode-stdio-1.0
+  main-is:            Main.hs
+  hs-source-dirs:     bench
+  build-depends:      hw-json-simple-cursor
+  other-modules:      Paths_hw_json_simple_cursor
diff --git a/src/HaskellWorks/Data/Json/Simple/Cursor.hs b/src/HaskellWorks/Data/Json/Simple/Cursor.hs
new file mode 100644
--- /dev/null
+++ b/src/HaskellWorks/Data/Json/Simple/Cursor.hs
@@ -0,0 +1,90 @@
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE InstanceSigs          #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE ScopedTypeVariables   #-}
+
+module HaskellWorks.Data.Json.Simple.Cursor
+  ( JsonCursor(..)
+  , jsonCursorPos
+  ) where
+
+import Data.String
+import Data.Word
+import HaskellWorks.Data.FromByteString
+import HaskellWorks.Data.FromForeignRegion
+import HaskellWorks.Data.Positioning
+import HaskellWorks.Data.RankSelect.Base.Rank0
+import HaskellWorks.Data.RankSelect.Base.Rank1
+import HaskellWorks.Data.RankSelect.Base.Select1
+import HaskellWorks.Data.RankSelect.CsPoppy
+import HaskellWorks.Data.TreeCursor
+import Prelude                                   hiding (drop)
+
+import qualified Data.ByteString                                as BS
+import qualified Data.ByteString.Char8                          as BSC
+import qualified Data.ByteString.Internal                       as BSI
+import qualified Data.Vector.Storable                           as DVS
+import qualified Foreign.ForeignPtr                             as F
+import qualified HaskellWorks.Data.BalancedParens               as BP
+import qualified HaskellWorks.Data.BalancedParens.RangeMin      as RM
+import qualified HaskellWorks.Data.Json.Simple.Cursor.SemiIndex as SI
+
+data JsonCursor t v w = JsonCursor
+  { cursorText     :: !t
+  , interests      :: !v
+  , balancedParens :: !w
+  , cursorRank     :: !Count
+  }
+  deriving (Eq, Show)
+
+instance FromByteString (JsonCursor BS.ByteString (DVS.Vector Word64) (BP.SimpleBalancedParens (DVS.Vector Word64))) where
+  fromByteString bs = JsonCursor
+    { cursorText      = bs
+    , interests       = ib
+    , balancedParens  = BP.SimpleBalancedParens bp
+    , cursorRank      = 1
+    }
+    where SI.SemiIndex _ ib bp = SI.buildSemiIndex bs
+
+instance FromByteString (JsonCursor BS.ByteString CsPoppy (RM.RangeMin CsPoppy)) where
+  fromByteString bs = JsonCursor
+    { cursorText      = bs
+    , interests       = makeCsPoppy ib
+    , balancedParens  = RM.mkRangeMin (makeCsPoppy bp)
+    , cursorRank      = 1
+    }
+    where SI.SemiIndex _ ib bp = SI.buildSemiIndex bs
+
+instance FromForeignRegion (JsonCursor BS.ByteString (DVS.Vector Word64) (BP.SimpleBalancedParens (DVS.Vector Word64))) where
+  fromForeignRegion (fptr, offset, size) = fromByteString (BSI.fromForeignPtr (F.castForeignPtr fptr) offset size)
+
+instance FromForeignRegion (JsonCursor BS.ByteString CsPoppy (RM.RangeMin CsPoppy)) where
+  fromForeignRegion (fptr, offset, size) = fromByteString (BSI.fromForeignPtr (F.castForeignPtr fptr) offset size)
+
+instance IsString (JsonCursor BS.ByteString (DVS.Vector Word64) (BP.SimpleBalancedParens (DVS.Vector Word64))) where
+  fromString = fromByteString . BSC.pack
+
+instance IsString (JsonCursor BS.ByteString CsPoppy (RM.RangeMin CsPoppy)) where
+  fromString = fromByteString . BSC.pack
+
+instance (BP.BalancedParens u, Rank1 u, Rank0 u) => TreeCursor (JsonCursor t v u) where
+  firstChild :: JsonCursor t v u -> Maybe (JsonCursor t v u)
+  firstChild k = let mq = BP.firstChild (balancedParens k) (cursorRank k) in (\q -> k { cursorRank = q }) <$> mq
+
+  nextSibling :: JsonCursor t v u -> Maybe (JsonCursor t v u)
+  nextSibling k = (\q -> k { cursorRank = q }) <$> BP.nextSibling (balancedParens k) (cursorRank k)
+
+  parent :: JsonCursor t v u -> Maybe (JsonCursor t v u)
+  parent k = let mq = BP.parent (balancedParens k) (cursorRank k) in (\q -> k { cursorRank = q }) <$> mq
+
+  depth :: JsonCursor t v u -> Maybe Count
+  depth k = BP.depth (balancedParens k) (cursorRank k)
+
+  subtreeSize :: JsonCursor t v u -> Maybe Count
+  subtreeSize k = BP.subtreeSize (balancedParens k) (cursorRank k)
+
+jsonCursorPos :: (Rank1 w, Select1 v) => JsonCursor s v w -> Position
+jsonCursorPos k = toPosition (select1 ik (rank1 bpk (cursorRank k)) - 1)
+  where ik  = interests k
+        bpk = balancedParens k
diff --git a/src/HaskellWorks/Data/Json/Simple/Cursor/Fast.hs b/src/HaskellWorks/Data/Json/Simple/Cursor/Fast.hs
new file mode 100644
--- /dev/null
+++ b/src/HaskellWorks/Data/Json/Simple/Cursor/Fast.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE FlexibleInstances    #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+
+module HaskellWorks.Data.Json.Simple.Cursor.Fast
+  ( fromByteString
+  , fromForeignRegion
+  , fromString
+  ) where
+
+import Foreign.ForeignPtr
+import HaskellWorks.Data.Json.Simple.Cursor
+import HaskellWorks.Data.RankSelect.CsPoppy
+
+import qualified Data.ByteString                                      as BS
+import qualified Data.ByteString.Char8                                as BSC
+import qualified Data.ByteString.Internal                             as BSI
+import qualified HaskellWorks.Data.BalancedParens.RangeMin            as RM
+import qualified HaskellWorks.Data.FromForeignRegion                  as F
+import qualified HaskellWorks.Data.Json.Simple.Cursor.Internal.IbBp   as J
+import qualified HaskellWorks.Data.Json.Simple.Cursor.Internal.ToIbBp as J
+
+fromByteString :: BS.ByteString -> JsonCursor BS.ByteString CsPoppy (RM.RangeMin CsPoppy)
+fromByteString bs = JsonCursor
+  { cursorText      = bs
+  , interests       = makeCsPoppy ib
+  , balancedParens  = RM.mkRangeMin (makeCsPoppy bp)
+  , cursorRank      = 1
+  }
+  where J.IbBp ib bp = J.toIbBp bs
+
+fromForeignRegion :: F.ForeignRegion -> JsonCursor BS.ByteString CsPoppy (RM.RangeMin CsPoppy)
+fromForeignRegion (fptr, offset, size) = fromByteString (BSI.fromForeignPtr (castForeignPtr fptr) offset size)
+
+fromString :: String -> JsonCursor BS.ByteString CsPoppy (RM.RangeMin CsPoppy)
+fromString = fromByteString . BSC.pack
diff --git a/src/HaskellWorks/Data/Json/Simple/Cursor/Internal/IbBp.hs b/src/HaskellWorks/Data/Json/Simple/Cursor/Internal/IbBp.hs
new file mode 100644
--- /dev/null
+++ b/src/HaskellWorks/Data/Json/Simple/Cursor/Internal/IbBp.hs
@@ -0,0 +1,10 @@
+module HaskellWorks.Data.Json.Simple.Cursor.Internal.IbBp where
+
+import Data.Word
+
+import qualified Data.Vector.Storable as DVS
+
+data IbBp = IbBp
+  { ib :: DVS.Vector Word64
+  , bp :: DVS.Vector Word64
+  }
diff --git a/src/HaskellWorks/Data/Json/Simple/Cursor/Internal/ToIbBp.hs b/src/HaskellWorks/Data/Json/Simple/Cursor/Internal/ToIbBp.hs
new file mode 100644
--- /dev/null
+++ b/src/HaskellWorks/Data/Json/Simple/Cursor/Internal/ToIbBp.hs
@@ -0,0 +1,12 @@
+module HaskellWorks.Data.Json.Simple.Cursor.Internal.ToIbBp where
+
+import qualified Data.ByteString                                    as BS
+import qualified HaskellWorks.Data.Json.Simple.Cursor.Internal.IbBp as Z
+import qualified HaskellWorks.Data.Json.Simple.Cursor.SemiIndex     as J
+
+class ToIbBp a where
+  toIbBp :: a -> Z.IbBp
+
+instance ToIbBp BS.ByteString where
+  toIbBp bs = Z.IbBp ib bp
+    where J.SemiIndex _ ib bp = J.buildSemiIndex bs
diff --git a/src/HaskellWorks/Data/Json/Simple/Cursor/Internal/Word8.hs b/src/HaskellWorks/Data/Json/Simple/Cursor/Internal/Word8.hs
new file mode 100644
--- /dev/null
+++ b/src/HaskellWorks/Data/Json/Simple/Cursor/Internal/Word8.hs
@@ -0,0 +1,72 @@
+module HaskellWorks.Data.Json.Simple.Cursor.Internal.Word8 where
+
+import Data.Word
+import Data.Word8 hiding (isDigit)
+
+import qualified Data.Char as C
+
+isLeadingDigit :: Word8 -> Bool
+isLeadingDigit w = w == _hyphen || (w >= _0 && w <= _9)
+
+isTrailingDigit :: Word8 -> Bool
+isTrailingDigit w = w == _plus || w == _hyphen || (w >= _0 && w <= _9) || w == _period || w == _E || w == _e
+
+isAlphabetic :: Word8 -> Bool
+isAlphabetic w = (w >= _A && w <= _Z) || (w >= _a && w <= _z)
+
+isDigit :: Word8 -> Bool
+isDigit w = w >= _0 && w <= _9
+
+wIsJsonNumberDigit :: Word8 -> Bool
+wIsJsonNumberDigit w = (w >= _0 && w <= _9) || w == _hyphen
+
+doubleQuote :: Word8
+doubleQuote = fromIntegral (C.ord '"')
+
+backSlash :: Word8
+backSlash = fromIntegral (C.ord '\\')
+
+openBrace :: Word8
+openBrace = fromIntegral (C.ord '{')
+
+closeBrace :: Word8
+closeBrace = fromIntegral (C.ord '}')
+
+openBracket :: Word8
+openBracket = fromIntegral (C.ord '[')
+
+closeBracket :: Word8
+closeBracket = fromIntegral (C.ord ']')
+
+comma :: Word8
+comma = fromIntegral (C.ord ',')
+
+colon :: Word8
+colon = fromIntegral (C.ord ':')
+
+isPeriod :: Word8 -> Bool
+isPeriod w = w == 46
+
+isMinus :: Word8 -> Bool
+isMinus w = w == 45
+
+isPlus :: Word8 -> Bool
+isPlus w = w == 43
+
+isValueChar :: Word8 -> Bool
+isValueChar c = isAlphabetic c || isDigit c || isPeriod c || isMinus c || isPlus c
+
+isOpen :: Word8 -> Bool
+isOpen c = c == openBracket || c == openBrace
+
+isClose :: Word8 -> Bool
+isClose c = c == closeBracket || c == closeBrace
+
+isDelim :: Word8 -> Bool
+isDelim c = c == comma || c == colon
+
+isDoubleQuote :: Word8 -> Bool
+isDoubleQuote c = c == doubleQuote
+
+isBackSlash :: Word8 -> Bool
+isBackSlash c = c == backSlash
diff --git a/src/HaskellWorks/Data/Json/Simple/Cursor/SemiIndex.hs b/src/HaskellWorks/Data/Json/Simple/Cursor/SemiIndex.hs
new file mode 100644
--- /dev/null
+++ b/src/HaskellWorks/Data/Json/Simple/Cursor/SemiIndex.hs
@@ -0,0 +1,80 @@
+{-# LANGUAGE DeriveFunctor     #-}
+{-# LANGUAGE DeriveTraversable #-}
+{-# LANGUAGE InstanceSigs      #-}
+{-# LANGUAGE MultiWayIf        #-}
+
+module HaskellWorks.Data.Json.Simple.Cursor.SemiIndex
+  ( buildSemiIndex
+  , SemiIndex(..)
+  ) where
+
+import Control.Monad.ST
+import Data.Word
+
+import qualified Data.ByteString                                     as BS
+import qualified Data.ByteString.Unsafe                              as BSU
+import qualified Data.Vector.Storable                                as DVS
+import qualified HaskellWorks.Data.Bits.Writer.Storable              as W
+import qualified HaskellWorks.Data.Json.Simple.Cursor.Internal.Word8 as W8
+
+{-# ANN module ("HLint: ignore Reduce duplication"  :: String) #-}
+{-# ANN module ("HLint: ignore Redundant do"        :: String) #-}
+
+data Context = InJson | InString | InEscape deriving (Eq, Show)
+
+data SemiIndex v = SemiIndex
+  { semiIndexContext :: !Context
+  , semiIndexIb      :: !v
+  , semiIndexBp      :: !v
+  } deriving (Functor, Traversable, Foldable)
+
+buildSemiIndex :: BS.ByteString -> SemiIndex (DVS.Vector Word64)
+buildSemiIndex bs = DVS.createT $ do
+  let len = (BS.length bs + 7) `div` 8
+  mib <- W.newWriter len
+  mbp <- W.newWriter (len * 2)
+  buildFromByteString mib mbp bs 0 InJson
+{-# INLINE buildSemiIndex #-}
+
+buildFromByteString :: W.Writer s -> W.Writer s -> BS.ByteString -> Int -> Context -> ST s (SemiIndex (DVS.MVector s Word64))
+buildFromByteString ib bp bs i context = if i < BS.length bs
+  then do
+    let c = BSU.unsafeIndex bs i
+    case context of
+      InJson -> if
+        | c == W8.openBracket || c == W8.openBrace -> do
+          W.unsafeWriteBit bp 1
+          W.unsafeWriteBit bp 1
+          W.unsafeWriteBit ib 1
+          buildFromByteString ib bp bs (i + 1) InJson
+        | c == W8.closeBracket || c == W8.closeBrace -> do
+          W.unsafeWriteBit bp 0
+          W.unsafeWriteBit bp 0
+          W.unsafeWriteBit ib 1
+          buildFromByteString ib bp bs (i + 1) InJson
+        | c == W8.comma || c == W8.colon -> do
+          W.unsafeWriteBit bp 0
+          W.unsafeWriteBit bp 1
+          W.unsafeWriteBit ib 1
+          buildFromByteString ib bp bs (i + 1) InJson
+        | c == W8.doubleQuote -> do
+          W.unsafeWriteBit ib 0
+          buildFromByteString ib bp bs (i + 1) InString
+        | otherwise -> do
+          W.unsafeWriteBit ib 0
+          buildFromByteString ib bp bs (i + 1) InJson
+      InString -> do
+        W.unsafeWriteBit ib 0
+        let newContext = if
+              | c == W8.doubleQuote  -> InJson
+              | c == W8.backSlash    -> InEscape
+              | otherwise           -> InString
+        buildFromByteString ib bp bs (i + 1) newContext
+      InEscape -> do
+        W.unsafeWriteBit ib 0
+        buildFromByteString ib bp bs (i + 1) InString
+  else do
+    ibv <- W.written ib
+    bpv <- W.written bp
+    return (SemiIndex context ibv bpv)
+{-# INLINE buildFromByteString #-}
diff --git a/src/HaskellWorks/Data/Json/Simple/Cursor/Snippet.hs b/src/HaskellWorks/Data/Json/Simple/Cursor/Snippet.hs
new file mode 100644
--- /dev/null
+++ b/src/HaskellWorks/Data/Json/Simple/Cursor/Snippet.hs
@@ -0,0 +1,32 @@
+module HaskellWorks.Data.Json.Simple.Cursor.Snippet
+  ( snippetPos
+  , snippet
+  ) where
+
+import Data.Maybe
+import HaskellWorks.Data.Json.Simple.Cursor
+import HaskellWorks.Data.Positioning
+import HaskellWorks.Data.RankSelect.Base.Select1
+import HaskellWorks.Data.RankSelect.CsPoppy
+
+import qualified Data.ByteString                           as BS
+import qualified HaskellWorks.Data.BalancedParens          as BP
+import qualified HaskellWorks.Data.BalancedParens.RangeMin as RM
+
+snippetPos :: JsonCursor BS.ByteString CsPoppy (RM.RangeMin CsPoppy) -> (Count, Count)
+snippetPos k = (kpa, kpz)
+  where kpa   = select1 kib kta + km
+        kpz   = select1 kib ktz - km
+        kib   = interests k
+        kbp   = balancedParens k
+        kra   = cursorRank k
+        krz   = fromMaybe maxBound (BP.findClose kbp kra)
+        ksa   = kra + 1
+        ksz   = krz + 1
+        kta   = ksa `div` 2
+        ktz   = ksz `div` 2
+        km    = ksa `mod` 2
+
+snippet :: JsonCursor BS.ByteString CsPoppy (RM.RangeMin CsPoppy) -> BS.ByteString
+snippet k = let (a, z) = snippetPos k in BS.take (fromIntegral (z - a + 1)) (BS.drop (fromIntegral (a - 1)) kt)
+  where kt    = cursorText k
diff --git a/test/HaskellWorks/Data/Json/Simple/CursorSpec.hs b/test/HaskellWorks/Data/Json/Simple/CursorSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/HaskellWorks/Data/Json/Simple/CursorSpec.hs
@@ -0,0 +1,84 @@
+{-# LANGUAGE ExplicitForAll            #-}
+{-# LANGUAGE FlexibleContexts          #-}
+{-# LANGUAGE FlexibleInstances         #-}
+{-# LANGUAGE InstanceSigs              #-}
+{-# LANGUAGE MultiParamTypeClasses     #-}
+{-# LANGUAGE NoMonomorphismRestriction #-}
+{-# LANGUAGE OverloadedStrings         #-}
+{-# LANGUAGE ScopedTypeVariables       #-}
+
+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
+
+module HaskellWorks.Data.Json.Simple.CursorSpec
+  ( spec
+  ) where
+
+import Control.Monad
+import HaskellWorks.Hspec.Hedgehog
+import Hedgehog
+import Test.Hspec
+
+import qualified HaskellWorks.Data.Json.Simple.Cursor         as Z
+import qualified HaskellWorks.Data.Json.Simple.Cursor.Fast    as FAST
+import qualified HaskellWorks.Data.Json.Simple.Cursor.Snippet as S
+import qualified HaskellWorks.Data.TreeCursor                 as TC
+
+{-# ANN module ("HLint: ignore Redundant do"        :: String) #-}
+{-# ANN module ("HLint: ignore Reduce duplication"  :: String) #-}
+{-# ANN module ("HLint: ignore Redundant bracket"   :: String) #-}
+
+fc = TC.firstChild
+ns = TC.nextSibling
+
+spec :: Spec
+spec = describe "HaskellWorks.Data.Json.Backend.Simple.CursorSpec" $ do
+  describe "Json cursor" $ do
+    describe "For sample Json" $ do
+      let k = FAST.fromByteString "[[11],[22]]"
+      -- [  [  1  1 ]  ,  [  2  2 ]  ]
+      -- (( ((      )) )( ((      )) ))
+      it "can navigate" $ requireTest $ do
+        (Z.cursorRank <$>  Just                            k) === Just 1
+        (Z.cursorRank <$>  ns                              k) === Nothing
+        (Z.cursorRank <$>  fc                              k) === Just 2
+        (Z.cursorRank <$> (fc >=> ns                     ) k) === Just 8
+        (Z.cursorRank <$> (fc >=> ns >=> fc              ) k) === Just 9
+        (Z.cursorRank <$> (fc >=> ns >=> fc >=> ns       ) k) === Nothing
+        (Z.cursorRank <$> (fc >=> ns >=> fc >=> fc       ) k) === Just 10
+        (Z.cursorRank <$> (fc >=> ns >=> fc >=> fc >=> ns) k) === Nothing
+        (Z.cursorRank <$> (fc >=> ns >=> fc >=> fc >=> fc) k) === Nothing
+        (Z.cursorRank <$> (fc >=> fc                     ) k) === Just 3
+        (Z.cursorRank <$> (fc >=> fc >=> ns              ) k) === Nothing
+        (Z.cursorRank <$> (fc >=> fc >=> fc              ) k) === Just 4
+        (Z.cursorRank <$> (fc >=> fc >=> fc >=> ns       ) k) === Nothing
+        (Z.cursorRank <$> (fc >=> fc >=> fc >=> fc       ) k) === Nothing
+      it "can snippet pos" $ requireTest $ do
+        (S.snippetPos <$>  Just                            k) === Just (1, 11)
+        (S.snippetPos <$>  ns                              k) === Nothing
+        (S.snippetPos <$>  fc                              k) === Just (2,  5)
+        (S.snippetPos <$> (fc >=> ns                     ) k) === Just (7, 10)
+        (S.snippetPos <$> (fc >=> ns >=> fc              ) k) === Just (7, 10)
+        (S.snippetPos <$> (fc >=> ns >=> fc >=> ns       ) k) === Nothing
+        (S.snippetPos <$> (fc >=> ns >=> fc >=> fc       ) k) === Just (8,  9)
+        (S.snippetPos <$> (fc >=> ns >=> fc >=> fc >=> ns) k) === Nothing
+        (S.snippetPos <$> (fc >=> ns >=> fc >=> fc >=> fc) k) === Nothing
+        (S.snippetPos <$> (fc >=> fc                     ) k) === Just (2,  5)
+        (S.snippetPos <$> (fc >=> fc >=> ns              ) k) === Nothing
+        (S.snippetPos <$> (fc >=> fc >=> fc              ) k) === Just (3,  4)
+        (S.snippetPos <$> (fc >=> fc >=> fc >=> ns       ) k) === Nothing
+        (S.snippetPos <$> (fc >=> fc >=> fc >=> fc       ) k) === Nothing
+      it "can snippet" $ requireTest $ do
+        (S.snippet <$>  Just                            k) === Just "[[11],[22]]"
+        (S.snippet <$>  ns                              k) === Nothing
+        (S.snippet <$>  fc                              k) === Just "[11]"
+        (S.snippet <$> (fc >=> ns                     ) k) === Just "[22]"
+        (S.snippet <$> (fc >=> ns >=> fc              ) k) === Just "[22]"
+        (S.snippet <$> (fc >=> ns >=> fc >=> ns       ) k) === Nothing
+        (S.snippet <$> (fc >=> ns >=> fc >=> fc       ) k) === Just "22"
+        (S.snippet <$> (fc >=> ns >=> fc >=> fc >=> ns) k) === Nothing
+        (S.snippet <$> (fc >=> ns >=> fc >=> fc >=> fc) k) === Nothing
+        (S.snippet <$> (fc >=> fc                     ) k) === Just "[11]"
+        (S.snippet <$> (fc >=> fc >=> ns              ) k) === Nothing
+        (S.snippet <$> (fc >=> fc >=> fc              ) k) === Just "11"
+        (S.snippet <$> (fc >=> fc >=> fc >=> ns       ) k) === Nothing
+        (S.snippet <$> (fc >=> fc >=> fc >=> fc       ) k) === Nothing
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
