Parse numbers inside strings

This was requested via #1481.
I have added a check if a number starts with the quote character `"`, so that we can move the char pointer accordingly (same way as when checking for negative sign at beginning of number). I have also added the quote character `"` as a valid ending character when parsing numbers. I think this should not be a problem, but I will let CI decide. However, since opening and closing quotes pair are validated separately, I do not think there can be a situation where this fails. In *valid* JSON,  there should never be a quote `"` after a number unless that number is in a string. Let me know if I am wrong though.
Fixes #1481

Provide the ability to decode strings as values (double/uint64 etc)

**Is your feature request related to a problem? Please describe.**
For most crypto exchanges, they have some values (doubles) that are enclosed in quotes. The type of those values are therefore defined as strings with a value in it and I would have to extract the string and run a method on the value outside of the API.

**Describe the solution you'd like**
Either add a way to tell the api that it should remove the quotes when applying the decoder or add a wrapper function that removes the quotes for the string which can then be decoded by the get_double().
By allowing this within the API, I don't have to resort to copying the string around for decoding in other libraries.

**Describe alternatives you've considered**
I can always use std::stof or the likes, but that is not as elegant. Ie there are tons of methods to convert strings to doubles or ints, but ideally I don't have to use something outside of the library for this.

**Additional context**
I don't think this is needed, the problem is pretty self explanatory.

** Are you willing to contribute code or documentation toward this new feature? **
Yes but you probably wouldn't find my code good enough.:) But for sure I could make a wrapper method that shrinks the stringview on either side of the view by a character which is then passed to the get_double method.

diff --git a/doc/basics.md b/doc/basics.md
index 740a9e05a7..2b91c49a4d 100644
--- a/doc/basics.md
+++ b/doc/basics.md
@@ -22,6 +22,7 @@ An overview of what you need to know to use simdjson, with examples.
 * [Rewinding](#rewinding)
 * [Direct Access to the Raw String](#direct-access-to-the-raw-string)
 * [Newline-Delimited JSON (ndjson) and JSON lines](#newline-delimited-json-ndjson-and-json-lines)
+* [Parsing Numbers Inside Strings](#parsing-numbers-inside-strings)
 * [Thread Safety](#thread-safety)
 * [Standard Compliance](#standard-compliance)

@@ -984,7 +985,7 @@ The `raw_json_token()` should be fast and free of allocation.
 Newline-Delimited JSON (ndjson) and JSON lines
 ----------------------------------------------

-The simdjson library also support multithreaded JSON streaming through a large file containing many
+The simdjson library also supports multithreaded JSON streaming through a large file containing many
 smaller JSON documents in either [ndjson](http://ndjson.org) or [JSON lines](http://jsonlines.org)
 format. If your JSON documents all contain arrays or objects, we even support direct file
 concatenation without whitespace. The concatenated file has no size restrictions (including larger
@@ -1016,6 +1017,105 @@ If your documents are large (e.g., larger than a megabyte), then the `iterate_ma

 See [iterate_many.md](iterate_many.md) for detailed information and design.

+
+
+Parsing Numbers Inside Strings
+------------------------------
+
+Though the JSON specification allows for numbers and string values, many engineers choose to integrate the numbers inside strings, e.g., they prefer `{"a":"1.9"}` to`{"a":1.9}`.
+The simdjson library supports parsing valid numbers inside strings which makes it more convenient for people working with those types of documents. This feature is supported through
+three methods: `get_double_in_string`, `get_int64_in_string` and  `get_uint64_in_string`. However, it is important to note that these methods are not substitute to the regular
+`get_double`, `get_int64` and `get_uint64`. The usage of the `get_*_in_string` methods is solely to parse valid JSON numbers inside strings, and so we expect users to call these
+methods appropriately. In particular, a valid JSON number has no leading and no trailing whitespaces, and the strings `"nan"`, `"1e"` and `"infinity"` will not be accepted as valid
+numbers. As an example, suppose we have the following JSON text:
+
+```c++
+auto json =
+{
+   "ticker":{
+      "base":"BTC",
+      "target":"USD",
+      "price":"443.7807865468",
+      "volume":"31720.1493969300",
+      "change":"Infinity",
+      "markets":[
+         {
+            "market":"bitfinex",
+            "price":"447.5000000000",
+            "volume":"10559.5293639000"
+         },
+         {
+            "market":"bitstamp",
+            "price":"448.5400000000",
+            "volume":"11628.2880079300"
+         },
+         {
+            "market":"btce",
+            "price":"432.8900000000",
+            "volume":"8561.0563600000"
+         }
+      ]
+   },
+   "timestamp":1399490941,
+   "timestampstr":"1399490941"
+}
+```
+
+Now, suppose that a user wants to get the time stamp from the `timestampstr` key. One could do the following:
+
+```c++
+ondemand::parser parser;
+auto doc = parser.iterate(json);
+uint64_t time = doc.at_pointer("/timestampstr").get_uint64_in_string();
+std::cout << time << std::endl;   // Prints 1399490941
+```
+
+Another thing a user might want to do is extract the `markets` array and get the market name, price and volume. Here is one way to do so:
+
+```c++
+ondemand::parser parser;
+auto doc = parser.iterate(json);
+
+// Getting markets array
+ondemand::array markets = doc.find_field("ticker").find_field("markets").get_array();
+// Iterating through markets array
+for (auto value : markets) {
+    std::cout << "Market: " << value.find_field("market").get_string();
+    std::cout << "\tPrice: " << value.find_field("price").get_double_in_string();
+    std::cout << "\tVolume: " << value.find_field("volume").get_double_in_string() << std::endl;
+}
+
+/* The above prints
+Market: bitfinex        Price: 447.5    Volume: 10559.5
+Market: bitstamp        Price: 448.54   Volume: 11628.3
+Market: btce    Price: 432.89   Volume: 8561.06
+*/
+```
+
+Finally, here is an example dealing with errors where the user wants to convert the string `"Infinity"`(`"change"` key) to a float with infinity value.
+
+```c++
+ondemand::parser parser;
+auto doc = parser.iterate(json);
+// Get "change"/"Infinity" key/value pair
+ondemand::value value = doc.find_field("ticker").find_field("change");
+double d;
+std::string_view view;
+auto error = value.get_double_in_string().get(d);
+// Check if parsed value into double successfully
+if (error) {
+  error = value.get_string().get(view);
+  if (error) { /* Handle error */ }
+  else if (view == "Infinity") {
+    d = std::numeric_limits::infinity();
+  }
+  else { /* Handle wrong value */ }
+}
+```
+It is also important to note that when dealing an invalid number inside a string, simdjson will report a `NUMBER_ERROR` error if the string begins with a number whereas simdjson
+will report a `INCORRECT_TYPE` error otherwise.
+
+
 Thread Safety
 -------------

diff --git a/include/simdjson/generic/numberparsing.h b/include/simdjson/generic/numberparsing.h
index 1abe524df9..e0f380619f 100644
--- a/include/simdjson/generic/numberparsing.h
+++ b/include/simdjson/generic/numberparsing.h
@@ -513,6 +513,9 @@ simdjson_really_inline error_code parse_number(const uint8_t *const, W &writer)
 simdjson_unused simdjson_really_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src) noexcept { return 0; }
 simdjson_unused simdjson_really_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src) noexcept { return 0; }
 simdjson_unused simdjson_really_inline simdjson_result<double> parse_double(const uint8_t * const src) noexcept { return 0; }
+simdjson_unused simdjson_really_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; }
+simdjson_unused simdjson_really_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t * const src) noexcept { return 0; }
+simdjson_unused simdjson_really_inline simdjson_result<double> parse_double_in_string(const uint8_t * const src) noexcept { return 0; }

 #else

@@ -773,6 +776,54 @@ simdjson_unused simdjson_really_inline simdjson_result<uint64_t> parse_unsigned(
   return i;
 }

+// Parse any number from 0 to 18,446,744,073,709,551,615
+simdjson_unused simdjson_really_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
+  const uint8_t *p = src + 1;
+  //
+  // Parse the integer part.
+  //
+  // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
+  const uint8_t *const start_digits = p;
+  uint64_t i = 0;
+  while (parse_digit(*p, i)) { p++; }
+
+  // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
+  // Optimization note: size_t is expected to be unsigned.
+  size_t digit_count = size_t(p - start_digits);
+  // The longest positive 64-bit number is 20 digits.
+  // We do it this way so we don't trigger this branch unless we must.
+  // Optimization note: the compiler can probably merge
+  // ((digit_count == 0) || (digit_count > 20))
+  // into a single  branch since digit_count is unsigned.
+  if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
+  // Here digit_count > 0.
+  if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
+  // We can do the following...
+  // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
+  //  return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
+  // }
+  // as a single table lookup:
+  if (*p != '"') { return NUMBER_ERROR; }
+
+  if (digit_count == 20) {
+    // Positive overflow check:
+    // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
+    //   biggest uint64_t.
+    // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
+    //   If we got here, it's a 20 digit number starting with the digit "1".
+    // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
+    //   than 1,553,255,926,290,448,384.
+    // - That is smaller than the smallest possible 20-digit number the user could write:
+    //   10,000,000,000,000,000,000.
+    // - Therefore, if the number is positive and lower than that, it's overflow.
+    // - The value we are looking at is less than or equal to 9,223,372,036,854,775,808 (INT64_MAX).
+    //
+    if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
+  }
+
+  return i;
+}
+
 // Parse any number from  -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
 simdjson_unused simdjson_really_inline simdjson_result<int64_t> parse_integer(const uint8_t *src) noexcept {
   //
@@ -859,6 +910,48 @@ simdjson_unused simdjson_really_inline simdjson_result<int64_t> parse_integer(co
   return negative ? (~i+1) : i;
 }

+// Parse any number from  -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
+simdjson_unused simdjson_really_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
+  //
+  // Check for minus sign
+  //
+  bool negative = (*(src + 1) == '-');
+  const uint8_t *p = src + negative + 1;
+
+  //
+  // Parse the integer part.
+  //
+  // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
+  const uint8_t *const start_digits = p;
+  uint64_t i = 0;
+  while (parse_digit(*p, i)) { p++; }
+
+  // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
+  // Optimization note: size_t is expected to be unsigned.
+  size_t digit_count = size_t(p - start_digits);
+  // We go from
+  // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
+  // so we can never represent numbers that have more than 19 digits.
+  size_t longest_digit_count = 19;
+  // Optimization note: the compiler can probably merge
+  // ((digit_count == 0) || (digit_count > longest_digit_count))
+  // into a single  branch since digit_count is unsigned.
+  if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
+  // Here digit_count > 0.
+  if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
+  // We can do the following...
+  // if (!jsoncharutils::is_structural_or_whitespace(*p)) {
+  //  return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
+  // }
+  // as a single table lookup:
+  if(*p != '"') { return NUMBER_ERROR; }
+  // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
+  // Performance note: This check is only needed when digit_count == longest_digit_count but it is
+  // so cheap that we might as well always make it.
+  if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
+  return negative ? (~i+1) : i;
+}
+
 simdjson_unused simdjson_really_inline simdjson_result<double> parse_double(const uint8_t * src) noexcept {
   //
   // Check for minus sign
@@ -1020,6 +1113,83 @@ simdjson_unused simdjson_really_inline simdjson_result<double> parse_double(cons
   return d;
 }

+simdjson_unused simdjson_really_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
+  //
+  // Check for minus sign
+  //
+  bool negative = (*(src + 1) == '-');
+  src += negative + 1;
+
+  //
+  // Parse the integer part.
+  //
+  uint64_t i = 0;
+  const uint8_t *p = src;
+  p += parse_digit(*p, i);
+  bool leading_zero = (i == 0);
+  while (parse_digit(*p, i)) { p++; }
+  // no integer digits, or 0123 (zero must be solo)
+  if ( p == src ) { return INCORRECT_TYPE; }
+  if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
+
+  //
+  // Parse the decimal part.
+  //
+  int64_t exponent = 0;
+  bool overflow;
+  if (simdjson_likely(*p == '.')) {
+    p++;
+    const uint8_t *start_decimal_digits = p;
+    if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
+    p++;
+    while (parse_digit(*p, i)) { p++; }
+    exponent = -(p - start_decimal_digits);
+
+    // Overflow check. More than 19 digits (minus the decimal) may be overflow.
+    overflow = p-src-1 > 19;
+    if (simdjson_unlikely(overflow && leading_zero)) {
+      // Skip leading 0.00000 and see if it still overflows
+      const uint8_t *start_digits = src + 2;
+      while (*start_digits == '0') { start_digits++; }
+      overflow = start_digits-src > 19;
+    }
+  } else {
+    overflow = p-src > 19;
+  }
+
+  //
+  // Parse the exponent
+  //
+  if (*p == 'e' || *p == 'E') {
+    p++;
+    bool exp_neg = *p == '-';
+    p += exp_neg || *p == '+';
+
+    uint64_t exp = 0;
+    const uint8_t *start_exp_digits = p;
+    while (parse_digit(*p, exp)) { p++; }
+    // no exp digits, or 20+ exp digits
+    if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
+
+    exponent += exp_neg ? 0-exp : exp;
+  }
+
+  if (*p != '"') { return NUMBER_ERROR; }
+
+  overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
+
+  //
+  // Assemble (or slow-parse) the float
+  //
+  double d;
+  if (simdjson_likely(!overflow)) {
+    if (compute_float_64(exponent, i, negative, d)) { return d; }
+  }
+  if (!parse_float_fallback(src-negative, &d)) {
+    return NUMBER_ERROR;
+  }
+  return d;
+}
 } //namespace {}
 #endif // SIMDJSON_SKIPNUMBERPARSING

diff --git a/include/simdjson/generic/ondemand/document-inl.h b/include/simdjson/generic/ondemand/document-inl.h
index 045411d501..7ebf39c275 100644
--- a/include/simdjson/generic/ondemand/document-inl.h
+++ b/include/simdjson/generic/ondemand/document-inl.h
@@ -64,12 +64,21 @@ simdjson_really_inline simdjson_result<object> document::get_object() & noexcept
 simdjson_really_inline simdjson_result<uint64_t> document::get_uint64() noexcept {
   return get_root_value_iterator().get_root_uint64();
 }
+simdjson_really_inline simdjson_result<uint64_t> document::get_uint64_in_string() noexcept {
+  return get_root_value_iterator().get_root_uint64_in_string();
+}
 simdjson_really_inline simdjson_result<int64_t> document::get_int64() noexcept {
   return get_root_value_iterator().get_root_int64();
 }
+simdjson_really_inline simdjson_result<int64_t> document::get_int64_in_string() noexcept {
+  return get_root_value_iterator().get_root_int64_in_string();
+}
 simdjson_really_inline simdjson_result<double> document::get_double() noexcept {
   return get_root_value_iterator().get_root_double();
 }
+simdjson_really_inline simdjson_result<double> document::get_double_in_string() noexcept {
+  return get_root_value_iterator().get_root_double_in_string();
+}
 simdjson_really_inline simdjson_result<std::string_view> document::get_string() noexcept {
   return get_root_value_iterator().get_root_string();
 }
diff --git a/include/simdjson/generic/ondemand/document.h b/include/simdjson/generic/ondemand/document.h
index 2d65fc9cfd..3ea363499d 100644
--- a/include/simdjson/generic/ondemand/document.h
+++ b/include/simdjson/generic/ondemand/document.h
@@ -53,6 +53,13 @@ class document {
    * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
    */
   simdjson_really_inline simdjson_result<uint64_t> get_uint64() noexcept;
+  /**
+   * Cast this JSON value (inside string) to an unsigned integer.
+   *
+   * @returns A signed 64-bit integer.
+   * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
+   */
+  simdjson_really_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
   /**
    * Cast this JSON value to a signed integer.
    *
@@ -60,6 +67,13 @@ class document {
    * @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
    */
   simdjson_really_inline simdjson_result<int64_t> get_int64() noexcept;
+  /**
+   * Cast this JSON value (inside string) to a signed integer.
+   *
+   * @returns A signed 64-bit integer.
+   * @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
+   */
+  simdjson_really_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
   /**
    * Cast this JSON value to a double.
    *
@@ -67,6 +81,14 @@ class document {
    * @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
    */
   simdjson_really_inline simdjson_result<double> get_double() noexcept;
+
+  /**
+   * Cast this JSON value (inside string) to a double.
+   *
+   * @returns A double.
+   * @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
+   */
+  simdjson_really_inline simdjson_result<double> get_double_in_string() noexcept;
   /**
    * Cast this JSON value to a string.
    *
@@ -408,6 +430,7 @@ struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document> : public SIM
   simdjson_really_inline simdjson_result<uint64_t> get_uint64() noexcept;
   simdjson_really_inline simdjson_result<int64_t> get_int64() noexcept;
   simdjson_really_inline simdjson_result<double> get_double() noexcept;
+  simdjson_really_inline simdjson_result<double> get_double_from_string() noexcept;
   simdjson_really_inline simdjson_result<std::string_view> get_string() noexcept;
   simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
   simdjson_really_inline simdjson_result<bool> get_bool() noexcept;
diff --git a/include/simdjson/generic/ondemand/value-inl.h b/include/simdjson/generic/ondemand/value-inl.h
index 5b6e4fbcdf..95f6a3492d 100644
--- a/include/simdjson/generic/ondemand/value-inl.h
+++ b/include/simdjson/generic/ondemand/value-inl.h
@@ -36,12 +36,21 @@ simdjson_really_inline simdjson_result<std::string_view> value::get_string() noe
 simdjson_really_inline simdjson_result<double> value::get_double() noexcept {
   return iter.get_double();
 }
+simdjson_really_inline simdjson_result<double> value::get_double_in_string() noexcept {
+  return iter.get_double_in_string();
+}
 simdjson_really_inline simdjson_result<uint64_t> value::get_uint64() noexcept {
   return iter.get_uint64();
 }
+simdjson_really_inline simdjson_result<uint64_t> value::get_uint64_in_string() noexcept {
+  return iter.get_uint64_in_string();
+}
 simdjson_really_inline simdjson_result<int64_t> value::get_int64() noexcept {
   return iter.get_int64();
 }
+simdjson_really_inline simdjson_result<int64_t> value::get_int64_in_string() noexcept {
+  return iter.get_int64_in_string();
+}
 simdjson_really_inline simdjson_result<bool> value::get_bool() noexcept {
   return iter.get_bool();
 }
@@ -221,14 +230,26 @@ simdjson_really_inline simdjson_result<uint64_t> simdjson_result<SIMDJSON_IMPLEM
   if (error()) { return error(); }
   return first.get_uint64();
 }
+simdjson_really_inline simdjson_result<uint64_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_uint64_in_string() noexcept {
+  if (error()) { return error(); }
+  return first.get_uint64_in_string();
+}
 simdjson_really_inline simdjson_result<int64_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_int64() noexcept {
   if (error()) { return error(); }
   return first.get_int64();
 }
+simdjson_really_inline simdjson_result<int64_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_int64_in_string() noexcept {
+  if (error()) { return error(); }
+  return first.get_int64_in_string();
+}
 simdjson_really_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_double() noexcept {
   if (error()) { return error(); }
   return first.get_double();
 }
+simdjson_really_inline simdjson_result<double> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_double_in_string() noexcept {
+  if (error()) { return error(); }
+  return first.get_double_in_string();
+}
 simdjson_really_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::get_string() noexcept {
   if (error()) { return error(); }
   return first.get_string();
diff --git a/include/simdjson/generic/ondemand/value.h b/include/simdjson/generic/ondemand/value.h
index 65de6bc6f7..edd6d2b640 100644
--- a/include/simdjson/generic/ondemand/value.h
+++ b/include/simdjson/generic/ondemand/value.h
@@ -69,11 +69,19 @@ class value {
   /**
    * Cast this JSON value to an unsigned integer.
    *
-   * @returns A signed 64-bit integer.
+   * @returns A unsigned 64-bit integer.
    * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
    */
   simdjson_really_inline simdjson_result<uint64_t> get_uint64() noexcept;

+  /**
+   * Cast this JSON value (inside string) to a unsigned integer.
+   *
+   * @returns A unsigned 64-bit integer.
+   * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
+   */
+  simdjson_really_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
+
   /**
    * Cast this JSON value to a signed integer.
    *
@@ -82,6 +90,14 @@ class value {
    */
   simdjson_really_inline simdjson_result<int64_t> get_int64() noexcept;

+  /**
+   * Cast this JSON value (inside string) to a signed integer.
+   *
+   * @returns A signed 64-bit integer.
+   * @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
+   */
+  simdjson_really_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
+
   /**
    * Cast this JSON value to a double.
    *
@@ -90,6 +106,14 @@ class value {
    */
   simdjson_really_inline simdjson_result<double> get_double() noexcept;

+  /**
+   * Cast this JSON value (inside string) to a double
+   *
+   * @returns A double.
+   * @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
+   */
+  simdjson_really_inline simdjson_result<double> get_double_in_string() noexcept;
+
   /**
    * Cast this JSON value to a string.
    *
@@ -416,8 +440,11 @@ struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> : public SIMDJS
   simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object> get_object() noexcept;

   simdjson_really_inline simdjson_result<uint64_t> get_uint64() noexcept;
+  simdjson_really_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
   simdjson_really_inline simdjson_result<int64_t> get_int64() noexcept;
+  simdjson_really_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
   simdjson_really_inline simdjson_result<double> get_double() noexcept;
+  simdjson_really_inline simdjson_result<double> get_double_in_string() noexcept;
   simdjson_really_inline simdjson_result<std::string_view> get_string() noexcept;
   simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::raw_json_string> get_raw_json_string() noexcept;
   simdjson_really_inline simdjson_result<bool> get_bool() noexcept;
diff --git a/include/simdjson/generic/ondemand/value_iterator-inl.h b/include/simdjson/generic/ondemand/value_iterator-inl.h
index 258e75cf42..7f3cd84ab3 100644
--- a/include/simdjson/generic/ondemand/value_iterator-inl.h
+++ b/include/simdjson/generic/ondemand/value_iterator-inl.h
@@ -452,16 +452,31 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> value_iter
   if(result.error() != INCORRECT_TYPE) { advance_non_root_scalar("uint64"); }
   return result;
 }
+simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> value_iterator::get_uint64_in_string() noexcept {
+  auto result = numberparsing::parse_unsigned_in_string(peek_non_root_scalar("uint64"));
+  if(result.error() != INCORRECT_TYPE) { advance_non_root_scalar("uint64"); }
+  return result;
+}
 simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> value_iterator::get_int64() noexcept {
   auto result = numberparsing::parse_integer(peek_non_root_scalar("int64"));
   if(result.error() != INCORRECT_TYPE) { advance_non_root_scalar("int64"); }
   return result;
 }
+simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> value_iterator::get_int64_in_string() noexcept {
+  auto result = numberparsing::parse_integer_in_string(peek_non_root_scalar("int64"));
+  if(result.error() != INCORRECT_TYPE) { advance_non_root_scalar("int64"); }
+  return result;
+}
 simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterator::get_double() noexcept {
   auto result = numberparsing::parse_double(peek_non_root_scalar("double"));
   if(result.error() != INCORRECT_TYPE) { advance_non_root_scalar("double"); }
   return result;
 }
+simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterator::get_double_in_string() noexcept {
+  auto result = numberparsing::parse_double_in_string(peek_non_root_scalar("double"));
+  if(result.error() != INCORRECT_TYPE) { advance_non_root_scalar("double"); }
+  return result;
+}
 simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::get_bool() noexcept {
   auto result = parse_bool(peek_non_root_scalar("bool"));
   if(result.error() != INCORRECT_TYPE) { advance_non_root_scalar("bool"); }
@@ -493,6 +508,18 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> value_iter
   if(result.error() != INCORRECT_TYPE) { advance_root_scalar("uint64"); }
   return result;
 }
+simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string() noexcept {
+  auto max_len = peek_start_length();
+  auto json = peek_root_scalar("uint64");
+  uint8_t tmpbuf[20+1]; // <20 digits> is the longest possible unsigned integer
+  if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) {
+    logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+    return NUMBER_ERROR;
+  }
+  auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
+  if(result.error() != INCORRECT_TYPE) { advance_root_scalar("uint64"); }
+  return result;
+}
 simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> value_iterator::get_root_int64() noexcept {
   auto max_len = peek_start_length();
   auto json = peek_root_scalar("int64");
@@ -506,6 +533,19 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> value_itera
   if(result.error() != INCORRECT_TYPE) { advance_root_scalar("int64"); }
   return result;
 }
+simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string() noexcept {
+  auto max_len = peek_start_length();
+  auto json = peek_root_scalar("int64");
+  uint8_t tmpbuf[20+1]; // -<19 digits> is the longest possible integer
+  if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) {
+    logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
+    return NUMBER_ERROR;
+  }
+
+  auto result = numberparsing::parse_integer_in_string(tmpbuf);
+  if(result.error() != INCORRECT_TYPE) { advance_root_scalar("int64"); }
+  return result;
+}
 simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterator::get_root_double() noexcept {
   auto max_len = peek_start_length();
   auto json = peek_root_scalar("double");
@@ -521,6 +561,21 @@ simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterat
   if(result.error() != INCORRECT_TYPE) { advance_root_scalar("double"); }
   return result;
 }
+simdjson_warn_unused simdjson_really_inline simdjson_result<double> value_iterator::get_root_double_in_string() noexcept {
+  auto max_len = peek_start_length();
+  auto json = peek_root_scalar("double");
+  // Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
+  // 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
+  // number: -0.<fraction>e-308.
+  uint8_t tmpbuf[1074+8+1];
+  if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) {
+    logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
+    return NUMBER_ERROR;
+  }
+  auto result = numberparsing::parse_double_in_string(tmpbuf);
+  if(result.error() != INCORRECT_TYPE) { advance_root_scalar("double"); }
+  return result;
+}
 simdjson_warn_unused simdjson_really_inline simdjson_result<bool> value_iterator::get_root_bool() noexcept {
   auto max_len = peek_start_length();
   auto json = peek_root_scalar("bool");
diff --git a/include/simdjson/generic/ondemand/value_iterator.h b/include/simdjson/generic/ondemand/value_iterator.h
index e742198a44..f9b2bbb805 100644
--- a/include/simdjson/generic/ondemand/value_iterator.h
+++ b/include/simdjson/generic/ondemand/value_iterator.h
@@ -283,16 +283,22 @@ class value_iterator {
   simdjson_warn_unused simdjson_really_inline simdjson_result<std::string_view> get_string() noexcept;
   simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
   simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> get_uint64() noexcept;
+  simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
   simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> get_int64() noexcept;
+  simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
   simdjson_warn_unused simdjson_really_inline simdjson_result<double> get_double() noexcept;
+  simdjson_warn_unused simdjson_really_inline simdjson_result<double> get_double_in_string() noexcept;
   simdjson_warn_unused simdjson_really_inline simdjson_result<bool> get_bool() noexcept;
   simdjson_really_inline bool is_null() noexcept;

   simdjson_warn_unused simdjson_really_inline simdjson_result<std::string_view> get_root_string() noexcept;
   simdjson_warn_unused simdjson_really_inline simdjson_result<raw_json_string> get_root_raw_json_string() noexcept;
   simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> get_root_uint64() noexcept;
+  simdjson_warn_unused simdjson_really_inline simdjson_result<uint64_t> get_root_uint64_in_string() noexcept;
   simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> get_root_int64() noexcept;
+  simdjson_warn_unused simdjson_really_inline simdjson_result<int64_t> get_root_int64_in_string() noexcept;
   simdjson_warn_unused simdjson_really_inline simdjson_result<double> get_root_double() noexcept;
+  simdjson_warn_unused simdjson_really_inline simdjson_result<double> get_root_double_in_string() noexcept;
   simdjson_warn_unused simdjson_really_inline simdjson_result<bool> get_root_bool() noexcept;
   simdjson_really_inline bool is_root_null() noexcept;

diff --git a/tests/ondemand/CMakeLists.txt b/tests/ondemand/CMakeLists.txt
index f120fc2483..9773a9a7db 100644
--- a/tests/ondemand/CMakeLists.txt
+++ b/tests/ondemand/CMakeLists.txt
@@ -13,6 +13,7 @@ add_cpp_test(ondemand_json_pointer_tests     LABELS ondemand acceptance per_impl
 add_cpp_test(ondemand_key_string_tests       LABELS ondemand acceptance per_implementation)
 add_cpp_test(ondemand_misc_tests             LABELS ondemand acceptance per_implementation)
 add_cpp_test(ondemand_number_tests           LABELS ondemand acceptance per_implementation)
+add_cpp_test(ondemand_number_in_string_tests LABELS ondemand acceptance per_implementation)
 add_cpp_test(ondemand_object_tests           LABELS ondemand acceptance per_implementation)
 add_cpp_test(ondemand_object_error_tests     LABELS ondemand acceptance per_implementation)
 add_cpp_test(ondemand_ordering_tests         LABELS ondemand acceptance per_implementation)
diff --git a/tests/ondemand/ondemand_number_in_string_tests.cpp b/tests/ondemand/ondemand_number_in_string_tests.cpp
new file mode 100644
index 0000000000..7e43645b57
--- /dev/null
+++ b/tests/ondemand/ondemand_number_in_string_tests.cpp
@@ -0,0 +1,273 @@
+#include "simdjson.h"
+#include "test_ondemand.h"
+#include <string>
+
+using namespace simdjson;
+
+namespace number_in_string_tests {
+    const padded_string CRYPTO_JSON = R"(
+    {
+        "ticker":{
+            "base":"BTC",
+            "target":"USD",
+            "price":"443.7807865468",
+            "volume":"31720.1493969300",
+            "change":"Infinity",
+            "markets":[
+                {
+                    "market":"bitfinex",
+                    "price":"447.5000000000",
+                    "volume":"10559.5293639000"
+                },
+                {
+                    "market":"bitstamp",
+                    "price":"448.5400000000",
+                    "volume":"11628.2880079300"
+                },
+                {
+                    "market":"btce",
+                    "price":"432.8900000000",
+                    "volume":"8561.0563600000"
+                }
+            ]
+        },
+        "timestamp":1399490941,
+        "timestampstr":"1399490941"
+    }
+    )"_padded;
+
+    bool array_double() {
+        TEST_START();
+        auto json = R"(["1.2","2.3","-42.3","2.43442e3", "-1.234e3"])"_padded;
+        ondemand::parser parser;
+        ondemand::document doc;
+        ASSERT_SUCCESS(parser.iterate(json).get(doc));
+        size_t counter{0};
+        std::vector<double> expected = {1.2, 2.3, -42.3, 2434.42, -1234};
+        double d;
+        for (auto value : doc) {
+            ASSERT_SUCCESS(value.get_double_in_string().get(d));
+            ASSERT_EQUAL(d,expected[counter++]);
+        }
+        TEST_SUCCEED();
+    }
+
+    bool array_int() {
+        TEST_START();
+        auto json = R"(["1", "2", "-3", "1000", "-7844"])"_padded;
+        ondemand::parser parser;
+        ondemand::document doc;
+        ASSERT_SUCCESS(parser.iterate(json).get(doc));
+        size_t counter{0};
+        std::vector<int> expected = {1, 2, -3, 1000, -7844};
+        int64_t i;
+        for (auto value : doc) {
+            ASSERT_SUCCESS(value.get_int64_in_string().get(i));
+            ASSERT_EQUAL(i,expected[counter++]);
+        }
+        TEST_SUCCEED();
+    }
+
+    bool array_unsigned() {
+        TEST_START();
+        auto json = R"(["1", "2", "24", "9000", "156934"])"_padded;
+        ondemand::parser parser;
+        ondemand::document doc;
+        ASSERT_SUCCESS(parser.iterate(json).get(doc));
+        size_t counter{0};
+        std::vector<int> expected = {1, 2, 24, 9000, 156934};
+        uint64_t u;
+        for (auto value : doc) {
+            ASSERT_SUCCESS(value.get_uint64_in_string().get(u));
+            ASSERT_EQUAL(u,expected[counter++]);
+        }
+        TEST_SUCCEED();
+    }
+
+    bool object() {
+        TEST_START();
+        auto json = R"({"a":"1.2", "b":"-2.342e2", "c":"22", "d":"-112358", "e":"1080", "f":"123456789"})"_padded;
+        ondemand::parser parser;
+        ondemand::document doc;
+        ASSERT_SUCCESS(parser.iterate(json).get(doc));
+        size_t counter{0};
+        std::vector<double> expected = {1.2, -234.2, 22, -112358, 1080, 123456789};
+        double d;
+        int64_t i;
+        uint64_t u;
+        // Doubles
+        ASSERT_SUCCESS(doc.find_field("a").get_double_in_string().get(d));
+        ASSERT_EQUAL(d,expected[counter++]);
+        ASSERT_SUCCESS(doc.find_field("b").get_double_in_string().get(d));
+        ASSERT_EQUAL(d,expected[counter++]);
+        // Integers
+        ASSERT_SUCCESS(doc.find_field("c").get_int64_in_string().get(i));
+        ASSERT_EQUAL(i,expected[counter++]);
+        ASSERT_SUCCESS(doc.find_field("d").get_int64_in_string().get(i));
+        ASSERT_EQUAL(i,expected[counter++]);
+        // Unsigned integers
+        ASSERT_SUCCESS(doc.find_field("e").get_uint64_in_string().get(u));
+        ASSERT_EQUAL(u,expected[counter++]);
+        ASSERT_SUCCESS(doc.find_field("f").get_uint64_in_string().get(u));
+        ASSERT_EQUAL(u,expected[counter++]);
+        TEST_SUCCEED();
+    }
+
+    bool docs() {
+        TEST_START();
+        auto double_doc = R"( "-1.23e1" )"_padded;
+        auto int_doc = R"( "-243" )"_padded;
+        auto uint_doc = R"( "212213" )"_padded;
+        ondemand::parser parser;
+        ondemand::document doc;
+        double d;
+        int64_t i;
+        uint64_t u;
+        // Double
+        ASSERT_SUCCESS(parser.iterate(double_doc).get(doc));
+        ASSERT_SUCCESS(doc.get_double_in_string().get(d));
+        ASSERT_EQUAL(d,-12.3);
+        // Integer
+        ASSERT_SUCCESS(parser.iterate(int_doc).get(doc));
+        ASSERT_SUCCESS(doc.get_int64_in_string().get(i));
+        ASSERT_EQUAL(i,-243);
+        // Unsinged integer
+        ASSERT_SUCCESS(parser.iterate(uint_doc).get(doc));
+        ASSERT_SUCCESS(doc.get_uint64_in_string().get(u));
+        ASSERT_EQUAL(u,212213);
+        TEST_SUCCEED();
+    }
+
+    bool number_parsing_error() {
+        TEST_START();
+        auto json = R"( ["13.06.54", "1.0e", "2e3r4,,."])"_padded;
+        ondemand::parser parser;
+        ondemand::document doc;
+        ASSERT_SUCCESS(parser.iterate(json).get(doc));
+        size_t counter{0};
+        std::string expected[3] = {"13.06.54", "1.0e", "2e3r4,,."};
+        for (auto value : doc) {
+            double d;
+            std::string_view view;
+            ASSERT_ERROR(value.get_double_in_string().get(d),NUMBER_ERROR);
+            ASSERT_SUCCESS(value.get_string().get(view));
+            ASSERT_EQUAL(view,expected[counter++]);
+        }
+        ASSERT_EQUAL(counter,3);
+        TEST_SUCCEED();
+    }
+
+    bool incorrect_type_error() {
+        TEST_START();
+        auto json = R"( ["e", "i", "pi", "one", "zero"])"_padded;
+        ondemand::parser parser;
+        ondemand::document doc;
+        ASSERT_SUCCESS(parser.iterate(json).get(doc));
+        size_t counter{0};
+        std::string expected[5] = {"e", "i", "pi", "one", "zero"};
+        for (auto value : doc) {
+            double d;
+            std::string_view view;
+            ASSERT_ERROR(value.get_double_in_string().get(d),INCORRECT_TYPE);
+            ASSERT_SUCCESS(value.get_string().get(view));
+            ASSERT_EQUAL(view,expected[counter++]);
+        }
+        ASSERT_EQUAL(counter,5);
+        TEST_SUCCEED();
+    }
+
+    bool json_pointer_test() {
+        TEST_START();
+        auto json = R"( ["12.34", { "a":["3","5.6"], "b":{"c":"1.23e1"} }, ["1", "3.5"] ])"_padded;
+        ondemand::parser parser;
+        ondemand::document doc;
+        ASSERT_SUCCESS(parser.iterate(json).get(doc));
+        std::vector<double> expected = {12.34, 5.6, 12.3, 1, 3.5};
+        size_t counter{0};
+        double d;
+        ASSERT_SUCCESS(doc.at_pointer("/0").get_double_in_string().get(d));
+        ASSERT_EQUAL(d,expected[counter++]);
+        ASSERT_SUCCESS(doc.at_pointer("/1/a/1").get_double_in_string().get(d));
+        ASSERT_EQUAL(d,expected[counter++]);
+        ASSERT_SUCCESS(doc.at_pointer("/1/b/c").get_double_in_string().get(d));
+        ASSERT_EQUAL(d,expected[counter++]);
+        ASSERT_SUCCESS(doc.at_pointer("/2/0").get_double_in_string().get(d));
+        ASSERT_EQUAL(d,expected[counter++]);
+        ASSERT_SUCCESS(doc.at_pointer("/2/1").get_double_in_string().get(d));
+        ASSERT_EQUAL(d,expected[counter++]);
+        ASSERT_EQUAL(counter,5);
+        TEST_SUCCEED();
+    }
+
+    bool crypto_timestamp() {
+        TEST_START();
+        ondemand::parser parser;
+        ondemand::document doc;
+        ASSERT_SUCCESS(parser.iterate(CRYPTO_JSON).get(doc));
+        uint64_t u;
+        ASSERT_SUCCESS(doc.at_pointer("/timestampstr").get_uint64_in_string().get(u));
+        ASSERT_EQUAL(u,1399490941);
+        TEST_SUCCEED();
+    }
+
+    bool crypto_market() {
+        TEST_START();
+        ondemand::parser parser;
+        ondemand::document doc;
+        ASSERT_SUCCESS(parser.iterate(CRYPTO_JSON).get(doc));
+        ondemand::array markets;
+        ASSERT_SUCCESS(doc.find_field("ticker").find_field("markets").get_array().get(markets));
+        std::string_view expected_views[3] = {"bitfinex", "bitstamp", "btce"};
+        double expected_prices[3] = {447.5, 448.54, 432.89};
+        double expected_volumes[3] = {10559.5293639, 11628.28800793, 8561.05636};
+        size_t counter{0};
+        for (auto value : markets) {
+            std::string_view view;
+            double price;
+            double volume;
+            ASSERT_SUCCESS(value.find_field("market").get_string().get(view));
+            ASSERT_EQUAL(view,expected_views[counter]);
+            ASSERT_SUCCESS(value.find_field("price").get_double_in_string().get(price));
+            ASSERT_EQUAL(price,expected_prices[counter]);
+            ASSERT_SUCCESS(value.find_field("volume").get_double_in_string().get(volume));
+            ASSERT_EQUAL(volume,expected_volumes[counter]);
+            counter++;
+        }
+        ASSERT_EQUAL(counter,3);
+        TEST_SUCCEED();
+    }
+
+    bool crypto_infinity() {
+        TEST_START();
+        ondemand::parser parser;
+        ondemand::document doc;
+        ASSERT_SUCCESS(parser.iterate(CRYPTO_JSON).get(doc));
+        ondemand::value value;
+        double d;
+        std::string_view view;
+        ASSERT_SUCCESS(doc.find_field("ticker").find_field("change").get(value));
+        ASSERT_ERROR(value.get_double_in_string().get(d), INCORRECT_TYPE);
+        ASSERT_SUCCESS(value.get_string().get(view));
+        ASSERT_EQUAL(view,"Infinity");
+        TEST_SUCCEED();
+    }
+
+    bool run() {
+        return  array_double() &&
+                array_int() &&
+                array_unsigned() &&
+                object() &&
+                docs() &&
+                number_parsing_error() &&
+                incorrect_type_error() &&
+                json_pointer_test() &&
+                crypto_timestamp() &&
+                crypto_market() &&
+                crypto_infinity() &&
+                true;
+    }
+}   // number_in_string_tests
+
+int main(int argc, char *argv[]) {
+  return test_main(argc, argv, number_in_string_tests::run);
+}
\ No newline at end of file


Add automatic rewind for at_pointer

`at_pointer` now automatically rewinds each call (as per #1622). The rewind is done at the beginning of the call, before any parsing is done. This is something we would want to mention in the documentation. Furthermore, it will also be important to mention to the user that `rewind` invalidates certain values (objects/arrays), and so it is important to store/consume them independently between each call of `at_pointer` which rewinds automatically now. This PR just implements the automatic rewind feature, the documentation issue will be fixed by #1618.

Fixes #1622

at_pointer in On Demand should automatically rewind

Currently, users of the On Demand API who use at_pointer (JSON Pointer) have to manually rewind.

Why don't we make that automatic?

diff --git a/include/simdjson/generic/ondemand/array.h b/include/simdjson/generic/ondemand/array.h
index 27d06078c6..28fa8b6624 100644
--- a/include/simdjson/generic/ondemand/array.h
+++ b/include/simdjson/generic/ondemand/array.h
@@ -54,7 +54,7 @@ class array {
    *   auto doc = parser.iterate(json);
    *   doc.at_pointer("/0/foo/a/1") == 20
    *
-   * Note that at_pointer() does not automatically rewind between each call (call rewind() to reset).
+   * Note that at_pointer() automatically calls rewind between each call.
    * Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
    * @return The value associated with the given JSON pointer, or:
    *         - NO_SUCH_FIELD if a field does not exist in an object
diff --git a/include/simdjson/generic/ondemand/document-inl.h b/include/simdjson/generic/ondemand/document-inl.h
index fc158694a8..75b0cd295f 100644
--- a/include/simdjson/generic/ondemand/document-inl.h
+++ b/include/simdjson/generic/ondemand/document-inl.h
@@ -135,6 +135,7 @@ simdjson_really_inline simdjson_result<std::string_view> document::raw_json_toke
 }

 simdjson_really_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
+  rewind(); // Rewind the document each time at_pointer is called
   if (json_pointer.empty()) {
     return this->resume_value();
   }
diff --git a/include/simdjson/generic/ondemand/document.h b/include/simdjson/generic/ondemand/document.h
index edc269484e..9abcd73896 100644
--- a/include/simdjson/generic/ondemand/document.h
+++ b/include/simdjson/generic/ondemand/document.h
@@ -335,7 +335,7 @@ class document {
    *   auto doc = parser.iterate(json);
    *   doc.at_pointer("//a/1") == 20
    *
-   * Note that at_pointer() does not automatically rewind between each call (call rewind() to reset).
+   * Note that at_pointer() automatically calls rewind between each call.
    * Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
    *
    * @return The value associated with the given JSON pointer, or:
diff --git a/include/simdjson/generic/ondemand/object.h b/include/simdjson/generic/ondemand/object.h
index 2eb71c977f..c92bbf79db 100644
--- a/include/simdjson/generic/ondemand/object.h
+++ b/include/simdjson/generic/ondemand/object.h
@@ -91,7 +91,7 @@ class object {
    *   auto doc = parser.iterate(json);
    *   doc.at_pointer("//a/1") == 20
    *
-   * Note that at_pointer() does not automatically rewind between each call (call rewind() to reset).
+   * Note that at_pointer() automatically calls rewind between each call.
    * Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
    *
    * @return The value associated with the given JSON pointer, or:
diff --git a/include/simdjson/generic/ondemand/value.h b/include/simdjson/generic/ondemand/value.h
index 631c254f1a..e2b092e79f 100644
--- a/include/simdjson/generic/ondemand/value.h
+++ b/include/simdjson/generic/ondemand/value.h
@@ -330,7 +330,7 @@ class value {
    *   auto doc = parser.iterate(json);
    *   doc.at_pointer("//a/1") == 20
    *
-   * Note that at_pointer() does not automatically rewind between each call (call rewind() to reset).
+   * Note that at_pointer() automatically calls rewind between each call.
    * Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
    *
    * @return The value associated with the given JSON pointer, or:

diff --git a/tests/ondemand/ondemand_json_pointer_tests.cpp b/tests/ondemand/ondemand_json_pointer_tests.cpp
index 9ee29454ad..ac79716aac 100644
--- a/tests/ondemand/ondemand_json_pointer_tests.cpp
+++ b/tests/ondemand/ondemand_json_pointer_tests.cpp
@@ -117,7 +117,6 @@ namespace json_pointer_tests {
             std::string json_pointer = "/" + std::to_string(i) + "/tire_pressure/1";
             ASSERT_SUCCESS(cars.at_pointer(json_pointer).get(x));
             measured.push_back(x);
-            cars.rewind();
         }

         std::vector<double> expected = {39.9, 31, 30};


Add JSON Pointer for On Demand

This implements JSON pointer for On Demand which works like for DOM. Currently, I have two tests that seem to work and I messed around a little bit and the JSON pointer also  work as expected. I will add more tests to test for success as well as failure (with the corresponding expected error).

A few remarks on the implementation:
* The implementation is almost entirely based on the implementation used for DOM, but I did not include the part where the at_pointer method checks for an empty *json_pointer* string because `array::at_pointer` and `object::at_pointer` already check if the string is empty and if we need to proceed further. And since the IEFT states that each element of the JSON path is only prefixed by a `/`, I was not able to imagine a situation where `at_pointer` would be called with an empty string other than if directly passed an empty string. Do correct me if I am wrong.
* Since `array::at_pointer` and `object::at_pointer` are technically recursive methods, I was not able to give them the attribute **simdjson_really_inline**. What I found is that either both `array::at_pointer` and `object::at_pointer` are not **simdjson_really_inline** or only `value::at_pointer` is not **simdjson_really_inline**. I have yet to compare the performance of both options.

Any feedback is appreciated.

Fixes #1427

Implement JSON Pointer on top of On Demand

[Currently our JSON Pointer API assumes a DOM tree](https://github.com/simdjson/simdjson/blob/c5def8f7060a7e9519c7f38e2c4ddf2dc784d6a8/doc/basics.md#json-pointer), but it is obvious that we can do it with On Demand.

If the pointer points at an array or an object, you'd like to be able to do...

```C++
    auto doc = parser.iterate(json);
    for (auto x : doc.at_pointer(insert json pointer expression)) {

    }
```

It could have substantial performance gains.

[The `at_pointer` functions in our DOM API have a rather clean implementation](https://github.com/simdjson/simdjson/blob/95b4870e20be5f97d9dcf63b23b1c6f520c366c1/include/simdjson/dom/object.h). It is then a matter of 'porting' them to the On Demand front-end. There will be some fun challenges.

diff --git a/include/simdjson/generic/ondemand/array-inl.h b/include/simdjson/generic/ondemand/array-inl.h
index c0fc42ce6e..4c1cf56031 100644
--- a/include/simdjson/generic/ondemand/array-inl.h
+++ b/include/simdjson/generic/ondemand/array-inl.h
@@ -93,6 +93,51 @@ simdjson_really_inline simdjson_result<size_t> array::count_elements() & noexcep
   return count;
 }

+inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) noexcept {
+  if (json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
+  json_pointer = json_pointer.substr(1);
+  // - means "the append position" or "the element after the end of the array"
+  // We don't support this, because we're returning a real element, not a position.
+  if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
+
+  // Read the array index
+  size_t array_index = 0;
+  size_t i;
+  for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) {
+    uint8_t digit = uint8_t(json_pointer[i] - '0');
+    // Check for non-digit in array index. If it's there, we're trying to get a field in an object
+    if (digit > 9) { return INCORRECT_TYPE; }
+    array_index = array_index*10 + digit;
+  }
+
+  // 0 followed by other digits is invalid
+  if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0"
+
+  // Empty string is invalid; so is a "/" with no digits before it
+  if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index"
+  // Get the child
+  auto child = at(array_index);
+  // If there is an error, it ends here
+  if(child.error()) {
+    return child;
+  }
+
+  // If there is a /, we're not done yet, call recursively.
+  if (i < json_pointer.length()) {
+    child = child.at_pointer(json_pointer.substr(i));
+  }
+  return child;
+}
+
+simdjson_really_inline simdjson_result<value> array::at(size_t index) noexcept {
+  size_t i=0;
+  for (auto value : *this) {
+    if (i == index) { return value; }
+    i++;
+  }
+  return INDEX_OUT_OF_BOUNDS;
+}
+
 } // namespace ondemand
 } // namespace SIMDJSON_IMPLEMENTATION
 } // namespace simdjson
@@ -126,4 +171,8 @@ simdjson_really_inline  simdjson_result<size_t> simdjson_result<SIMDJSON_IMPLEME
   if (error()) { return error(); }
   return first.count_elements();
 }
+simdjson_really_inline  simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::at_pointer(std::string_view json_pointer) noexcept {
+  if (error()) { return error(); }
+  return first.at_pointer(json_pointer);
+}
 } // namespace simdjson
diff --git a/include/simdjson/generic/ondemand/array.h b/include/simdjson/generic/ondemand/array.h
index 3336ccfd34..27d06078c6 100644
--- a/include/simdjson/generic/ondemand/array.h
+++ b/include/simdjson/generic/ondemand/array.h
@@ -43,6 +43,27 @@ class array {
    * safe to continue.
    */
   simdjson_really_inline simdjson_result<size_t> count_elements() & noexcept;
+
+  /**
+   * Get the value associated with the given JSON pointer.  We use the RFC 6901
+   * https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
+   * as the root of its own JSON document.
+   *
+   *   ondemand::parser parser;
+   *   auto json = R"([ { "foo": { "a": [ 10, 20, 30 ] }} ])"_padded;
+   *   auto doc = parser.iterate(json);
+   *   doc.at_pointer("/0/foo/a/1") == 20
+   *
+   * Note that at_pointer() does not automatically rewind between each call (call rewind() to reset).
+   * Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
+   * @return The value associated with the given JSON pointer, or:
+   *         - NO_SUCH_FIELD if a field does not exist in an object
+   *         - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
+   *         - INCORRECT_TYPE if a non-integer is used to access an array
+   *         - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
+   */
+  inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
+
 protected:
   /**
    * Begin array iteration.
@@ -80,6 +101,15 @@ class array {
    */
   simdjson_really_inline array(const value_iterator &iter) noexcept;

+  /**
+   * Get the value at the given index. This function has linear-time complexity.
+   * This function should only be called once as the array iterator is not reset between each call.
+   *
+   * @return The value at the given index, or:
+   *         - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
+   */
+  simdjson_really_inline simdjson_result<value> at(size_t index) noexcept;
+
   /**
    * Iterator marking current position.
    *
@@ -110,6 +140,7 @@ struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array> : public SIMDJS
   simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> begin() noexcept;
   simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> end() noexcept;
   simdjson_really_inline simdjson_result<size_t> count_elements() & noexcept;
+  simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
 };

 } // namespace simdjson
diff --git a/include/simdjson/generic/ondemand/document-inl.h b/include/simdjson/generic/ondemand/document-inl.h
index a5c0877b7b..fc158694a8 100644
--- a/include/simdjson/generic/ondemand/document-inl.h
+++ b/include/simdjson/generic/ondemand/document-inl.h
@@ -134,6 +134,23 @@ simdjson_really_inline simdjson_result<std::string_view> document::raw_json_toke
   return std::string_view(reinterpret_cast<const char*>(_iter.peek_start()), _iter.peek_start_length());
 }

+simdjson_really_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
+  if (json_pointer.empty()) {
+    return this->resume_value();
+  }
+  json_type t;
+  SIMDJSON_TRY(type().get(t));
+  switch (t)
+  {
+    case json_type::array:
+      return (*this).get_array().at_pointer(json_pointer);
+    case json_type::object:
+      return (*this).get_object().at_pointer(json_pointer);
+    default:
+      return INVALID_JSON_POINTER;
+  }
+}
+
 } // namespace ondemand
 } // namespace SIMDJSON_IMPLEMENTATION
 } // namespace simdjson
@@ -311,4 +328,9 @@ simdjson_really_inline simdjson_result<std::string_view> simdjson_result<SIMDJSO
   return first.raw_json_token();
 }

+simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
+  if (error()) { return error(); }
+  return first.at_pointer(json_pointer);
+}
+
 } // namespace simdjson
diff --git a/include/simdjson/generic/ondemand/document.h b/include/simdjson/generic/ondemand/document.h
index 581d8a7cef..edc269484e 100644
--- a/include/simdjson/generic/ondemand/document.h
+++ b/include/simdjson/generic/ondemand/document.h
@@ -318,6 +318,34 @@ class document {
    * Returns debugging information.
    */
   inline std::string to_debug_string() noexcept;
+
+  /**
+   * Get the value associated with the given JSON pointer.  We use the RFC 6901
+   * https://tools.ietf.org/html/rfc6901 standard.
+   *
+   *   ondemand::parser parser;
+   *   auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
+   *   auto doc = parser.iterate(json);
+   *   doc.at_pointer("/foo/a/1") == 20
+   *
+   * It is allowed for a key to be the empty string:
+   *
+   *   ondemand::parser parser;
+   *   auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
+   *   auto doc = parser.iterate(json);
+   *   doc.at_pointer("//a/1") == 20
+   *
+   * Note that at_pointer() does not automatically rewind between each call (call rewind() to reset).
+   * Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
+   *
+   * @return The value associated with the given JSON pointer, or:
+   *         - NO_SUCH_FIELD if a field does not exist in an object
+   *         - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
+   *         - INCORRECT_TYPE if a non-integer is used to access an array
+   *         - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
+   */
+  simdjson_really_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
+
 protected:
   simdjson_really_inline document(ondemand::json_iterator &&iter) noexcept;
   simdjson_really_inline const uint8_t *text(uint32_t idx) const noexcept;
@@ -396,6 +424,8 @@ struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::document> : public SIM

   /** @copydoc simdjson_really_inline std::string_view document::raw_json_token() const noexcept */
   simdjson_really_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+
+  simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
 };

 } // namespace simdjson
diff --git a/include/simdjson/generic/ondemand/object-inl.h b/include/simdjson/generic/ondemand/object-inl.h
index fa68959017..9a86cb71af 100644
--- a/include/simdjson/generic/ondemand/object-inl.h
+++ b/include/simdjson/generic/ondemand/object-inl.h
@@ -68,6 +68,46 @@ simdjson_really_inline simdjson_result<object_iterator> object::end() noexcept {
   return object_iterator(iter);
 }

+inline simdjson_result<value> object::at_pointer(std::string_view json_pointer) noexcept {
+  if (json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
+  json_pointer = json_pointer.substr(1);
+  size_t slash = json_pointer.find('/');
+  std::string_view key = json_pointer.substr(0, slash);
+  // Grab the child with the given key
+  simdjson_result<value> child;
+
+  // If there is an escape character in the key, unescape it and then get the child.
+  size_t escape = key.find('~');
+  if (escape != std::string_view::npos) {
+    // Unescape the key
+    std::string unescaped(key);
+    do {
+      switch (unescaped[escape+1]) {
+        case '0':
+          unescaped.replace(escape, 2, "~");
+          break;
+        case '1':
+          unescaped.replace(escape, 2, "/");
+          break;
+        default:
+          return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer");
+      }
+      escape = unescaped.find('~', escape+1);
+    } while (escape != std::string::npos);
+    child = find_field(unescaped);  // Take note find_field does not unescape keys when matching
+  } else {
+    child = find_field(key);
+  }
+  if(child.error()) {
+    return child; // we do not continue if there was an error
+  }
+  // If there is a /, we have to recurse and look up more of the path
+  if (slash != std::string_view::npos) {
+    child = child.at_pointer(json_pointer.substr(slash));
+  }
+  return child;
+}
+
 } // namespace ondemand
 } // namespace SIMDJSON_IMPLEMENTATION
 } // namespace simdjson
@@ -112,4 +152,9 @@ simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>
   return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::object>(first).find_field(key);
 }

+simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::at_pointer(std::string_view json_pointer) noexcept {
+  if (error()) { return error(); }
+  return first.at_pointer(json_pointer);
+}
+
 } // namespace simdjson
diff --git a/include/simdjson/generic/ondemand/object.h b/include/simdjson/generic/ondemand/object.h
index 5dd72fe9be..2eb71c977f 100644
--- a/include/simdjson/generic/ondemand/object.h
+++ b/include/simdjson/generic/ondemand/object.h
@@ -74,6 +74,34 @@ class object {
   /** @overload simdjson_really_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
   simdjson_really_inline simdjson_result<value> operator[](std::string_view key) && noexcept;

+  /**
+   * Get the value associated with the given JSON pointer. We use the RFC 6901
+   * https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
+   * as the root of its own JSON document.
+   *
+   *   ondemand::parser parser;
+   *   auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
+   *   auto doc = parser.iterate(json);
+   *   doc.at_pointer("/foo/a/1") == 20
+   *
+   * It is allowed for a key to be the empty string:
+   *
+   *   ondemand::parser parser;
+   *   auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
+   *   auto doc = parser.iterate(json);
+   *   doc.at_pointer("//a/1") == 20
+   *
+   * Note that at_pointer() does not automatically rewind between each call (call rewind() to reset).
+   * Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
+   *
+   * @return The value associated with the given JSON pointer, or:
+   *         - NO_SUCH_FIELD if a field does not exist in an object
+   *         - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
+   *         - INCORRECT_TYPE if a non-integer is used to access an array
+   *         - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
+   */
+  inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
+
 protected:
   static simdjson_really_inline simdjson_result<object> start(value_iterator &iter) noexcept;
   static simdjson_really_inline simdjson_result<object> start_root(value_iterator &iter) noexcept;
@@ -111,6 +139,7 @@ struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object> : public SIMDJ
   simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> find_field_unordered(std::string_view key) && noexcept;
   simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) & noexcept;
   simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> operator[](std::string_view key) && noexcept;
+  simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
 };

 } // namespace simdjson
diff --git a/include/simdjson/generic/ondemand/value-inl.h b/include/simdjson/generic/ondemand/value-inl.h
index 91ed6bac96..5b6e4fbcdf 100644
--- a/include/simdjson/generic/ondemand/value-inl.h
+++ b/include/simdjson/generic/ondemand/value-inl.h
@@ -135,6 +135,20 @@ simdjson_really_inline std::string_view value::raw_json_token() noexcept {
   return std::string_view(reinterpret_cast<const char*>(iter.peek_start()), iter.peek_start_length());
 }

+simdjson_really_inline simdjson_result<value> value::at_pointer(std::string_view json_pointer) noexcept {
+  json_type t;
+  SIMDJSON_TRY(type().get(t));
+  switch (t)
+  {
+    case json_type::array:
+      return (*this).get_array().at_pointer(json_pointer);
+    case json_type::object:
+      return (*this).get_object().at_pointer(json_pointer);
+    default:
+      return INVALID_JSON_POINTER;
+  }
+}
+
 } // namespace ondemand
 } // namespace SIMDJSON_IMPLEMENTATION
 } // namespace simdjson
@@ -296,4 +310,9 @@ simdjson_really_inline simdjson_result<std::string_view> simdjson_result<SIMDJSO
   return first.raw_json_token();
 }

+simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value>::at_pointer(std::string_view json_pointer) noexcept {
+  if (error()) { return error(); }
+  return first.at_pointer(json_pointer);
+}
+
 } // namespace simdjson
diff --git a/include/simdjson/generic/ondemand/value.h b/include/simdjson/generic/ondemand/value.h
index a8a5f48d6f..631c254f1a 100644
--- a/include/simdjson/generic/ondemand/value.h
+++ b/include/simdjson/generic/ondemand/value.h
@@ -314,6 +314,33 @@ class value {
    */
   simdjson_really_inline std::string_view raw_json_token() noexcept;

+  /**
+   * Get the value associated with the given JSON pointer.  We use the RFC 6901
+   * https://tools.ietf.org/html/rfc6901 standard.
+   *
+   *   ondemand::parser parser;
+   *   auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
+   *   auto doc = parser.iterate(json);
+   *   doc.at_pointer("/foo/a/1") == 20
+   *
+   * It is allowed for a key to be the empty string:
+   *
+   *   ondemand::parser parser;
+   *   auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
+   *   auto doc = parser.iterate(json);
+   *   doc.at_pointer("//a/1") == 20
+   *
+   * Note that at_pointer() does not automatically rewind between each call (call rewind() to reset).
+   * Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
+   *
+   * @return The value associated with the given JSON pointer, or:
+   *         - NO_SUCH_FIELD if a field does not exist in an object
+   *         - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length
+   *         - INCORRECT_TYPE if a non-integer is used to access an array
+   *         - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
+   */
+  simdjson_really_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
+
 protected:
   /**
    * Create a value.
@@ -459,6 +486,8 @@ struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> : public SIMDJS

   /** @copydoc simdjson_really_inline std::string_view value::raw_json_token() const noexcept */
   simdjson_really_inline simdjson_result<std::string_view> raw_json_token() noexcept;
+
+  simdjson_really_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
 };

 } // namespace simdjson

diff --git a/tests/ondemand/CMakeLists.txt b/tests/ondemand/CMakeLists.txt
index e1ee3d70d5..6373cb9cbc 100644
--- a/tests/ondemand/CMakeLists.txt
+++ b/tests/ondemand/CMakeLists.txt
@@ -2,14 +2,15 @@
 link_libraries(simdjson)
 include_directories(..)
 add_subdirectory(compilation_failure_tests)
-add_cpp_test(ondemand_tostring_tests           LABELS ondemand acceptance per_implementation)
+add_cpp_test(ondemand_tostring_tests         LABELS ondemand acceptance per_implementation)
 add_cpp_test(ondemand_active_tests           LABELS ondemand acceptance per_implementation)
 add_cpp_test(ondemand_array_tests            LABELS ondemand acceptance per_implementation)
 add_cpp_test(ondemand_array_error_tests      LABELS ondemand acceptance per_implementation)
 add_cpp_test(ondemand_compilation_tests      LABELS ondemand acceptance per_implementation)
 add_cpp_test(ondemand_error_tests            LABELS ondemand acceptance per_implementation)
+add_cpp_test(ondemand_json_pointer_tests     LABELS ondemand acceptance per_implementation)
 add_cpp_test(ondemand_key_string_tests       LABELS ondemand acceptance per_implementation)
-add_cpp_test(ondemand_misc_tests           LABELS ondemand acceptance per_implementation)
+add_cpp_test(ondemand_misc_tests             LABELS ondemand acceptance per_implementation)
 add_cpp_test(ondemand_number_tests           LABELS ondemand acceptance per_implementation)
 add_cpp_test(ondemand_object_tests           LABELS ondemand acceptance per_implementation)
 add_cpp_test(ondemand_object_error_tests     LABELS ondemand acceptance per_implementation)
diff --git a/tests/ondemand/ondemand_json_pointer_tests.cpp b/tests/ondemand/ondemand_json_pointer_tests.cpp
new file mode 100644
index 0000000000..9ee29454ad
--- /dev/null
+++ b/tests/ondemand/ondemand_json_pointer_tests.cpp
@@ -0,0 +1,168 @@
+#include "simdjson.h"
+#include "test_ondemand.h"
+#include <string>
+
+using namespace simdjson;
+
+namespace json_pointer_tests {
+    const padded_string TEST_JSON = R"(
+    {
+        "/~01abc": [
+        0,
+        {
+            "\\\" 0": [
+            "value0",
+            "value1"
+            ]
+        }
+        ],
+        "0": "0 ok",
+        "01": "01 ok",
+        "": "empty ok",
+        "arr": []
+    }
+    )"_padded;
+
+    const padded_string TEST_RFC_JSON = R"(
+    {
+        "foo": ["bar", "baz"],
+        "": 0,
+        "a/b": 1,
+        "c%d": 2,
+        "e^f": 3,
+        "g|h": 4,
+        "i\\j": 5,
+        "k\"l": 6,
+        " ": 7,
+        "m~n": 8
+    }
+    )"_padded;
+
+    bool run_success_test(const padded_string & json,std::string_view json_pointer,std::string expected) {
+        TEST_START();
+        ondemand::parser parser;
+        ondemand::document doc;
+        ondemand::value val;
+        std::string actual;
+        ASSERT_SUCCESS(parser.iterate(json).get(doc));
+        ASSERT_SUCCESS(doc.at_pointer(json_pointer).get(val));
+        ASSERT_SUCCESS(simdjson::to_string(val).get(actual));
+        ASSERT_EQUAL(actual,expected);
+        TEST_SUCCEED();
+    }
+
+    bool run_failure_test(const padded_string & json,std::string_view json_pointer,error_code expected) {
+        TEST_START();
+        ondemand::parser parser;
+        ondemand::document doc;
+        ASSERT_SUCCESS(parser.iterate(json).get(doc));
+        ASSERT_EQUAL(doc.at_pointer(json_pointer).error(),expected);
+        TEST_SUCCEED();
+    }
+
+    bool demo_test() {
+        TEST_START();
+        auto cars_json = R"( [
+        { "make": "Toyota", "model": "Camry",  "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
+        { "make": "Kia",    "model": "Soul",   "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
+        { "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
+        ] )"_padded;
+
+        ondemand::parser parser;
+        ondemand::document cars;
+        double x;
+        ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
+        ASSERT_SUCCESS(cars.at_pointer("/0/tire_pressure/1").get(x));
+        ASSERT_EQUAL(x,39.9);
+        TEST_SUCCEED();
+    }
+
+    bool demo_relative_path() {
+        TEST_START();
+        auto cars_json = R"( [
+        { "make": "Toyota", "model": "Camry",  "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
+        { "make": "Kia",    "model": "Soul",   "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
+        { "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
+        ] )"_padded;
+
+        ondemand::parser parser;
+        ondemand::document cars;
+        std::vector<double> measured;
+        ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
+        for (auto car_element : cars) {
+            double x;
+            ASSERT_SUCCESS(car_element.at_pointer("/tire_pressure/1").get(x));
+            measured.push_back(x);
+        }
+
+        std::vector<double> expected = {39.9, 31, 30};
+        if (measured != expected) { return false; }
+        TEST_SUCCEED();
+    }
+
+    bool many_json_pointers() {
+        TEST_START();
+        auto cars_json = R"( [
+        { "make": "Toyota", "model": "Camry",  "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
+        { "make": "Kia",    "model": "Soul",   "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
+        { "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
+        ] )"_padded;
+
+        ondemand::parser parser;
+        ondemand::document cars;
+        std::vector<double> measured;
+        ASSERT_SUCCESS(parser.iterate(cars_json).get(cars));
+        for (int i = 0; i < 3; i++) {
+            double x;
+            std::string json_pointer = "/" + std::to_string(i) + "/tire_pressure/1";
+            ASSERT_SUCCESS(cars.at_pointer(json_pointer).get(x));
+            measured.push_back(x);
+            cars.rewind();
+        }
+
+        std::vector<double> expected = {39.9, 31, 30};
+        if (measured != expected) { return false; }
+        TEST_SUCCEED();
+    }
+
+    bool run() {
+        return
+                demo_test() &&
+                demo_relative_path() &&
+                run_success_test(TEST_RFC_JSON,"",R"({"foo":["bar","baz"],"":0,"a/b":1,"c%d":2,"e^f":3,"g|h":4,"i\\j":5,"k\"l":6," ":7,"m~n":8})") &&
+                run_success_test(TEST_RFC_JSON,"/foo",R"(["bar","baz"])") &&
+                run_success_test(TEST_RFC_JSON,"/foo/0",R"("bar")") &&
+                run_success_test(TEST_RFC_JSON,"/",R"(0)") &&
+                run_success_test(TEST_RFC_JSON,"/a~1b",R"(1)") &&
+                run_success_test(TEST_RFC_JSON,"/c%d",R"(2)") &&
+                run_success_test(TEST_RFC_JSON,"/e^f",R"(3)") &&
+                run_success_test(TEST_RFC_JSON,"/g|h",R"(4)") &&
+                run_success_test(TEST_RFC_JSON,R"(/i\\j)",R"(5)") &&
+                run_success_test(TEST_RFC_JSON,R"(/k\"l)",R"(6)") &&
+                run_success_test(TEST_RFC_JSON,"/ ",R"(7)") &&
+                run_success_test(TEST_RFC_JSON,"/m~0n",R"(8)") &&
+                run_success_test(TEST_JSON, "", R"({"/~01abc":[0,{"\\\" 0":["value0","value1"]}],"0":"0 ok","01":"01 ok","":"empty ok","arr":[]})") &&
+                run_success_test(TEST_JSON, R"(/~1~001abc)", R"([0,{"\\\" 0":["value0","value1"]}])") &&
+                run_success_test(TEST_JSON, R"(/~1~001abc/1)", R"({"\\\" 0":["value0","value1"]})") &&
+                run_success_test(TEST_JSON, R"(/~1~001abc/1/\\\" 0)", R"(["value0","value1"])") &&
+                run_success_test(TEST_JSON, R"(/~1~001abc/1/\\\" 0/0)", "\"value0\"") &&
+                run_success_test(TEST_JSON, R"(/~1~001abc/1/\\\" 0/1)", "\"value1\"") &&
+                run_success_test(TEST_JSON, "/arr", R"([])") &&
+                run_success_test(TEST_JSON, "/0", "\"0 ok\"") &&
+                run_success_test(TEST_JSON, "/01", "\"01 ok\"") &&
+                run_success_test(TEST_JSON, "", R"({"/~01abc":[0,{"\\\" 0":["value0","value1"]}],"0":"0 ok","01":"01 ok","":"empty ok","arr":[]})") &&
+                run_failure_test(TEST_JSON, R"(/~1~001abc/1/\\\" 0/2)", INDEX_OUT_OF_BOUNDS) &&
+                run_failure_test(TEST_JSON, "/arr/0", INDEX_OUT_OF_BOUNDS) &&
+                run_failure_test(TEST_JSON, "~1~001abc", INVALID_JSON_POINTER) &&
+                run_failure_test(TEST_JSON, "/~01abc", NO_SUCH_FIELD) &&
+                run_failure_test(TEST_JSON, "/~1~001abc/01", INVALID_JSON_POINTER) &&
+                run_failure_test(TEST_JSON, "/~1~001abc/", INVALID_JSON_POINTER) &&
+                run_failure_test(TEST_JSON, "/~1~001abc/-", INDEX_OUT_OF_BOUNDS) &&
+                many_json_pointers() &&
+                true;
+    }
+}   // json_pointer_tests
+
+int main(int argc, char *argv[]) {
+  return test_main(argc, argv, json_pointer_tests::run);
+}
\ No newline at end of file


Make simdjson_result<element>.is() return bool

Fixes #708.

Rationale for is<T> when exceptions are disabled

When exceptions are disabled, then the is<T> template will return `simdjson_return<bool>`.  So we have to do something like...

```C++
 bool v;
 error_code x;
 doc.is<uint64_t>().tie(v,x);
```

That seems like a lot of work just to get a bool?

It seems much more useful to call directly `get<uint64_t>`.

diff --git a/include/simdjson/dom/element.h b/include/simdjson/dom/element.h
index 6d9af51ddd..acd466fca4 100644
--- a/include/simdjson/dom/element.h
+++ b/include/simdjson/dom/element.h
@@ -455,7 +455,7 @@ struct simdjson_result<dom::element> : public internal::simdjson_result_base<dom

   really_inline simdjson_result<dom::element_type> type() const noexcept;
   template<typename T>
-  really_inline simdjson_result<bool> is() const noexcept;
+  really_inline bool is() const noexcept;
   template<typename T>
   really_inline simdjson_result<T> get() const noexcept;
   template<typename T>
@@ -470,14 +470,14 @@ struct simdjson_result<dom::element> : public internal::simdjson_result_base<dom
   really_inline simdjson_result<double> get_double() const noexcept;
   really_inline simdjson_result<bool> get_bool() const noexcept;

-  really_inline simdjson_result<bool> is_array() const noexcept;
-  really_inline simdjson_result<bool> is_object() const noexcept;
-  really_inline simdjson_result<bool> is_string() const noexcept;
-  really_inline simdjson_result<bool> is_int64_t() const noexcept;
-  really_inline simdjson_result<bool> is_uint64_t() const noexcept;
-  really_inline simdjson_result<bool> is_double() const noexcept;
-  really_inline simdjson_result<bool> is_bool() const noexcept;
-  really_inline simdjson_result<bool> is_null() const noexcept;
+  really_inline bool is_array() const noexcept;
+  really_inline bool is_object() const noexcept;
+  really_inline bool is_string() const noexcept;
+  really_inline bool is_int64_t() const noexcept;
+  really_inline bool is_uint64_t() const noexcept;
+  really_inline bool is_double() const noexcept;
+  really_inline bool is_bool() const noexcept;
+  really_inline bool is_null() const noexcept;

   really_inline simdjson_result<dom::element> operator[](const std::string_view &key) const noexcept;
   really_inline simdjson_result<dom::element> operator[](const char *key) const noexcept;
diff --git a/include/simdjson/inline/element.h b/include/simdjson/inline/element.h
index 690f40ff6c..fc983eedb4 100644
--- a/include/simdjson/inline/element.h
+++ b/include/simdjson/inline/element.h
@@ -24,9 +24,8 @@ inline simdjson_result<dom::element_type> simdjson_result<dom::element>::type()
 }

 template<typename T>
-really_inline simdjson_result<bool> simdjson_result<dom::element>::is() const noexcept {
-  if (error()) { return error(); }
-  return first.is<T>();
+really_inline bool simdjson_result<dom::element>::is() const noexcept {
+  return !error() && first.is<T>();
 }
 template<typename T>
 really_inline simdjson_result<T> simdjson_result<dom::element>::get() const noexcept {
@@ -72,38 +71,30 @@ really_inline simdjson_result<bool> simdjson_result<dom::element>::get_bool() co
   return first.get_bool();
 }

-really_inline simdjson_result<bool> simdjson_result<dom::element>::is_array() const noexcept {
-  if (error()) { return error(); }
-  return first.is_array();
+really_inline bool simdjson_result<dom::element>::is_array() const noexcept {
+  return !error() && first.is_array();
 }
-really_inline simdjson_result<bool> simdjson_result<dom::element>::is_object() const noexcept {
-  if (error()) { return error(); }
-  return first.is_object();
+really_inline bool simdjson_result<dom::element>::is_object() const noexcept {
+  return !error() && first.is_object();
 }
-really_inline simdjson_result<bool> simdjson_result<dom::element>::is_string() const noexcept {
-  if (error()) { return error(); }
-  return first.is_string();
+really_inline bool simdjson_result<dom::element>::is_string() const noexcept {
+  return !error() && first.is_string();
 }
-really_inline simdjson_result<bool> simdjson_result<dom::element>::is_int64_t() const noexcept {
-  if (error()) { return error(); }
-  return first.is_int64_t();
+really_inline bool simdjson_result<dom::element>::is_int64_t() const noexcept {
+  return !error() && first.is_int64_t();
 }
-really_inline simdjson_result<bool> simdjson_result<dom::element>::is_uint64_t() const noexcept {
-  if (error()) { return error(); }
-  return first.is_uint64_t();
+really_inline bool simdjson_result<dom::element>::is_uint64_t() const noexcept {
+  return !error() && first.is_uint64_t();
 }
-really_inline simdjson_result<bool> simdjson_result<dom::element>::is_double() const noexcept {
-  if (error()) { return error(); }
-  return first.is_double();
+really_inline bool simdjson_result<dom::element>::is_double() const noexcept {
+  return !error() && first.is_double();
 }
-really_inline simdjson_result<bool> simdjson_result<dom::element>::is_bool() const noexcept {
-  if (error()) { return error(); }
-  return first.is_bool();
+really_inline bool simdjson_result<dom::element>::is_bool() const noexcept {
+  return !error() && first.is_bool();
 }

-really_inline simdjson_result<bool> simdjson_result<dom::element>::is_null() const noexcept {
-  if (error()) { return error(); }
-  return first.is_null();
+really_inline bool simdjson_result<dom::element>::is_null() const noexcept {
+  return !error() && first.is_null();
 }

 really_inline simdjson_result<dom::element> simdjson_result<dom::element>::operator[](const std::string_view &key) const noexcept {

diff --git a/tests/basictests.cpp b/tests/basictests.cpp
index d89eb5a9c0..ec7fe553bc 100644
--- a/tests/basictests.cpp
+++ b/tests/basictests.cpp
@@ -1503,27 +1503,9 @@ namespace type_tests {
     std::cout << "  test_is_null() expecting " << expected_is_null << std::endl;
     // Grab the element out and check success
     dom::element element = result.first;
-    bool actual_is_null;
-    auto error = result.is_null().get(actual_is_null);
-    ASSERT_SUCCESS(error);
-    ASSERT_EQUAL(actual_is_null, expected_is_null);
-
-    actual_is_null = element.is_null();
-    ASSERT_EQUAL(actual_is_null, expected_is_null);
-
-#if SIMDJSON_EXCEPTIONS
-
-    try {
-
-      actual_is_null = result.is_null();
-      ASSERT_EQUAL(actual_is_null, expected_is_null);
+    ASSERT_EQUAL(result.is_null(), expected_is_null);

-    } catch(simdjson_error &e) {
-      std::cerr << e.error() << std::endl;
-      return false;
-    }
-
-#endif // SIMDJSON_EXCEPTIONS
+    ASSERT_EQUAL(element.is_null(), expected_is_null);

     return true;
   }
diff --git a/tests/cast_tester.h b/tests/cast_tester.h
index 00e90de740..55eda96aec 100644
--- a/tests/cast_tester.h
+++ b/tests/cast_tester.h
@@ -40,7 +40,6 @@ class cast_tester {

   bool test_is(element element, bool expected);
   bool test_is(simdjson_result<element> element, bool expected);
-  bool test_is_error(simdjson_result<element> element, error_code expected_error);

   bool test_named_get(element element, T expected = {});
   bool test_named_get(simdjson_result<element> element, T expected = {});
@@ -49,13 +48,12 @@ class cast_tester {

   bool test_named_is(element element, bool expected);
   bool test_named_is(simdjson_result<element> element, bool expected);
-  bool test_named_is_error(simdjson_result<element> element, error_code expected_error);

 private:
   simdjson_result<T> named_get(element element);
   simdjson_result<T> named_get(simdjson_result<element> element);
   bool named_is(element element);
-  simdjson_result<bool> named_is(simdjson_result<element> element);
+  bool named_is(simdjson_result<element> element);
   bool assert_equal(const T& expected, const T& actual);
 };

@@ -206,16 +204,7 @@ bool cast_tester<T>::test_is(element element, bool expected) {

 template<typename T>
 bool cast_tester<T>::test_is(simdjson_result<element> element, bool expected) {
-  bool actual;
-  ASSERT_SUCCESS(element.is<T>().get(actual));
-  ASSERT_EQUAL(actual, expected);
-  return true;
-}
-
-template<typename T>
-bool cast_tester<T>::test_is_error(simdjson_result<element> element, error_code expected_error) {
-  UNUSED bool actual;
-  ASSERT_EQUAL(element.is<T>().get(actual), expected_error);
+  ASSERT_EQUAL(element.is<T>(), expected);
   return true;
 }

@@ -227,16 +216,7 @@ bool cast_tester<T>::test_named_is(element element, bool expected) {

 template<typename T>
 bool cast_tester<T>::test_named_is(simdjson_result<element> element, bool expected) {
-  bool actual;
-  ASSERT_SUCCESS(named_is(element).get(actual));
-  ASSERT_EQUAL(actual, expected);
-  return true;
-}
-
-template<typename T>
-bool cast_tester<T>::test_named_is_error(simdjson_result<element> element, error_code expected_error) {
-  bool actual;
-  ASSERT_EQUAL(named_is(element).get(actual), expected_error);
+  ASSERT_EQUAL(named_is(element), expected);
   return true;
 }

@@ -267,14 +247,14 @@ template<> bool cast_tester<int64_t>::named_is(element element) { return element
 template<> bool cast_tester<double>::named_is(element element) { return element.is_double(); }
 template<> bool cast_tester<bool>::named_is(element element) { return element.is_bool(); }

-template<> simdjson_result<bool> cast_tester<array>::named_is(simdjson_result<element> element) { return element.is_array(); }
-template<> simdjson_result<bool> cast_tester<object>::named_is(simdjson_result<element> element) { return element.is_object(); }
-template<> simdjson_result<bool> cast_tester<const char *>::named_is(simdjson_result<element> element) { return element.is_string(); }
-template<> simdjson_result<bool> cast_tester<std::string_view>::named_is(simdjson_result<element> element) { return element.is_string(); }
-template<> simdjson_result<bool> cast_tester<uint64_t>::named_is(simdjson_result<element> element) { return element.is_uint64_t(); }
-template<> simdjson_result<bool> cast_tester<int64_t>::named_is(simdjson_result<element> element) { return element.is_int64_t(); }
-template<> simdjson_result<bool> cast_tester<double>::named_is(simdjson_result<element> element) { return element.is_double(); }
-template<> simdjson_result<bool> cast_tester<bool>::named_is(simdjson_result<element> element) { return element.is_bool(); }
+template<> bool cast_tester<array>::named_is(simdjson_result<element> element) { return element.is_array(); }
+template<> bool cast_tester<object>::named_is(simdjson_result<element> element) { return element.is_object(); }
+template<> bool cast_tester<const char *>::named_is(simdjson_result<element> element) { return element.is_string(); }
+template<> bool cast_tester<std::string_view>::named_is(simdjson_result<element> element) { return element.is_string(); }
+template<> bool cast_tester<uint64_t>::named_is(simdjson_result<element> element) { return element.is_uint64_t(); }
+template<> bool cast_tester<int64_t>::named_is(simdjson_result<element> element) { return element.is_int64_t(); }
+template<> bool cast_tester<double>::named_is(simdjson_result<element> element) { return element.is_double(); }
+template<> bool cast_tester<bool>::named_is(simdjson_result<element> element) { return element.is_bool(); }

 template<typename T> bool cast_tester<T>::assert_equal(const T& expected, const T& actual) {
   ASSERT_EQUAL(expected, actual);