/// <summary> /// Find the start position of the desired item index. /// If the node knows it, the known value is returned. /// If not, the code seeks to the nearest node and reads enough of the document to find it. /// </summary> /// <param name="index">Index of array element to find</param> /// <param name="inputStreamProvider">Function which can open original file, if needed</param> /// <returns>Absolute byte offset of the start array[index] within this array</returns> public long FindArrayStart(int index, Func <Stream> inputStreamProvider) { if (index < 0 || index > this.Count) { throw new ArgumentOutOfRangeException("index"); } if (index == this.Count) { // If item after last is requested, return array end return(this.End); } else if (this.ArrayStarts != null && this.Every == 1) { // If we know every element position, just return it return(this.ArrayStarts[index]); } else if (index % this.Every == 0) { // If we know this element position, just return it return(this.ArrayStarts[index / this.Every]); } // Otherwise, find the closest span of the file we must read long readFromPosition; long readToPosition; int startIndex; if (this.ArrayStarts == null) { // If there are no array positions, we must read the whole array (it should be small) readFromPosition = this.Start + 1; readToPosition = this.End; startIndex = 0; } else { // If there are array positions, read from the nearest previous element available int startToRead = index / this.Every; readFromPosition = this.ArrayStarts[startToRead]; readToPosition = (this.ArrayStarts.Count > startToRead + 1 ? this.ArrayStarts[startToRead + 1] : this.End); startIndex = startToRead * this.Every; } using (Stream source = inputStreamProvider()) { int lengthToRead = (int)(1 + readToPosition - readFromPosition); byte[] buffer = new byte[lengthToRead + 1]; // Read the array slice source.Seek(readFromPosition, SeekOrigin.Begin); source.Read(buffer, 1, lengthToRead); // Make it a valid array prefix (it must start with '[', which will look like the root of the Json document buffer[0] = (byte)'['; using (JsonPositionedTextReader reader = new JsonPositionedTextReader(() => new MemoryStream(buffer))) { // Find the desired array item index in the buffer long relativePosition = reader.ReadToArrayIndex(index - startIndex); // Convert back to an absolute position (buffer[0] was (readFromPosition - 1) return((readFromPosition - 1) + relativePosition); } } }