コード例 #1
0
        /// <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);
                }
            }
        }