/// <summary>Encodes the given unprintable char codes in the given stream.</summary>
        /// <remarks>Encodes the given unprintable char codes in the given stream.</remarks>
        /// <exception cref="System.IO.IOException"></exception>
        private static void WriteUnprintableCodes(int charOffset, byte[] bytes, ByteUtil.ByteStream
                                                  unprintableCodes, GeneralLegacyIndexCodes.ExtraCodesStream extraCodes)
        {
            // the offset seems to be calculated based on the number of bytes in the
            // "extra codes" part of the entry (even if there are no extra codes bytes
            // actually written in the final entry).
            int unprintCharOffset = charOffset;

            if (extraCodes != null)
            {
                // we need to account for some extra codes which have not been written
                // yet.  additionally, any unprintable bytes added to the beginning of
                // the extra codes are ignored.
                unprintCharOffset = extraCodes.GetLength() + (charOffset - extraCodes.GetNumChars
                                                                  ()) - extraCodes.GetUnprintablePrefixLen();
            }
            // we write a whacky combo of bytes for each unprintable char which
            // includes a funky offset and extra char itself
            int offset = (UNPRINTABLE_COUNT_START + (UNPRINTABLE_COUNT_MULTIPLIER * unprintCharOffset
                                                     )) | UNPRINTABLE_OFFSET_FLAGS;

            // write offset as big-endian short
            unprintableCodes.Write((offset >> 8) & unchecked ((int)(0xFF)));
            unprintableCodes.Write(offset & unchecked ((int)(0xFF)));
            unprintableCodes.Write(UNPRINTABLE_MIDFIX);
            unprintableCodes.Write(bytes);
        }
        /// <summary>Encodes the given extra code info in the given stream.</summary>
        /// <remarks>Encodes the given extra code info in the given stream.</remarks>
        /// <exception cref="System.IO.IOException"></exception>
        private static void WriteExtraCodes(int charOffset, byte[] bytes, byte extraCodeModifier
                                            , GeneralLegacyIndexCodes.ExtraCodesStream extraCodes)
        {
            // we fill in a placeholder value for any chars w/out extra codes
            int numChars = extraCodes.GetNumChars();

            if (numChars < charOffset)
            {
                int fillChars = charOffset - numChars;
                extraCodes.WriteFill(fillChars, INTERNATIONAL_EXTRA_PLACEHOLDER);
                extraCodes.IncrementNumChars(fillChars);
            }
            if (bytes != null)
            {
                // write the actual extra codes and update the number of chars
                extraCodes.Write(bytes);
                extraCodes.IncrementNumChars(1);
            }
            else
            {
                // extra code modifiers modify the existing extra code bytes and do not
                // count as additional extra code chars
                int lastIdx = extraCodes.GetLength() - 1;
                if (lastIdx >= 0)
                {
                    // the extra code modifier is added to the last extra code written
                    byte lastByte = extraCodes.Get(lastIdx);
                    lastByte += extraCodeModifier;
                    extraCodes.Set(lastIdx, lastByte);
                }
                else
                {
                    // there is no previous extra code, add a new code (but keep track of
                    // this "unprintable code" prefix)
                    extraCodes.Write(extraCodeModifier);
                    extraCodes.SetUnprintablePrefixLen(1);
                }
            }
        }
        /// <summary>
        /// Converts an index value for a text column into the entry value (which
        /// is based on a variety of nifty codes).
        /// </summary>
        /// <remarks>
        /// Converts an index value for a text column into the entry value (which
        /// is based on a variety of nifty codes).
        /// </remarks>
        /// <exception cref="System.IO.IOException"></exception>
        internal virtual void WriteNonNullIndexTextValue(object value, ByteUtil.ByteStream
                                                         bout, bool isAscending)
        {
            // first, convert to string
            string str = Column.ToCharSequence(value).ToString();

            // all text columns (including memos) are only indexed up to the max
            // number of chars in a VARCHAR column
            if (str.Length > MAX_TEXT_INDEX_CHAR_LENGTH)
            {
                str = Sharpen.Runtime.Substring(str, 0, MAX_TEXT_INDEX_CHAR_LENGTH);
            }
            // record pprevious entry length so we can do any post-processing
            // necessary for this entry (handling descending)
            int prevLength = bout.GetLength();

            // now, convert each character to a "code" of one or more bytes
            GeneralLegacyIndexCodes.ExtraCodesStream extraCodes = null;
            ByteUtil.ByteStream unprintableCodes = null;
            ByteUtil.ByteStream crazyCodes       = null;
            int charOffset = 0;

            for (int i = 0; i < str.Length; ++i)
            {
                char c = str[i];
                GeneralLegacyIndexCodes.CharHandler ch = GetCharHandler(c);
                int    curCharOffset = charOffset;
                byte[] bytes         = ch.GetInlineBytes();
                if (bytes != null)
                {
                    // write the "inline" codes immediately
                    bout.Write(bytes);
                    // only increment the charOffset for chars with inline codes
                    ++charOffset;
                }
                if (ch.GetEncodingType() == GeneralLegacyIndexCodes.Type.SIMPLE)
                {
                    // common case, skip further code handling
                    continue;
                }
                bytes = ch.GetExtraBytes();
                byte extraCodeModifier = ch.GetExtraByteModifier();
                if ((bytes != null) || (extraCodeModifier != 0))
                {
                    if (extraCodes == null)
                    {
                        extraCodes = new GeneralLegacyIndexCodes.ExtraCodesStream(str.Length);
                    }
                    // keep track of the extra codes for later
                    WriteExtraCodes(curCharOffset, bytes, extraCodeModifier, extraCodes);
                }
                bytes = ch.GetUnprintableBytes();
                if (bytes != null)
                {
                    if (unprintableCodes == null)
                    {
                        unprintableCodes = new ByteUtil.ByteStream();
                    }
                    // keep track of the unprintable codes for later
                    WriteUnprintableCodes(curCharOffset, bytes, unprintableCodes, extraCodes);
                }
                byte crazyFlag = ch.GetCrazyFlag();
                if (crazyFlag != 0)
                {
                    if (crazyCodes == null)
                    {
                        crazyCodes = new ByteUtil.ByteStream();
                    }
                    // keep track of the crazy flags for later
                    crazyCodes.Write(crazyFlag);
                }
            }
            // write end text flag
            bout.Write(END_TEXT);
            bool hasExtraCodes = TrimExtraCodes(extraCodes, unchecked ((byte)0), INTERNATIONAL_EXTRA_PLACEHOLDER
                                                );
            bool hasUnprintableCodes = (unprintableCodes != null);
            bool hasCrazyCodes       = (crazyCodes != null);

            if (hasExtraCodes || hasUnprintableCodes || hasCrazyCodes)
            {
                // we write all the international extra bytes first
                if (hasExtraCodes)
                {
                    extraCodes.WriteTo(bout);
                }
                if (hasCrazyCodes || hasUnprintableCodes)
                {
                    // write 2 more end flags
                    bout.Write(END_TEXT);
                    bout.Write(END_TEXT);
                    // next come the crazy flags
                    if (hasCrazyCodes)
                    {
                        WriteCrazyCodes(crazyCodes, bout);
                        // if we are writing unprintable codes after this, tack on another
                        // code
                        if (hasUnprintableCodes)
                        {
                            bout.Write(CRAZY_CODES_UNPRINT_SUFFIX);
                        }
                    }
                    // then we write all the unprintable extra bytes
                    if (hasUnprintableCodes)
                    {
                        // write another end flag
                        bout.Write(END_TEXT);
                        unprintableCodes.WriteTo(bout);
                    }
                }
            }
            // handle descending order by inverting the bytes
            if (!isAscending)
            {
                // we actually write the end byte before flipping the bytes, and write
                // another one after flipping
                bout.Write(END_EXTRA_TEXT);
                // flip the bytes that we have written thus far for this text value
                IndexData.FlipBytes(bout.GetBytes(), prevLength, (bout.GetLength() - prevLength));
            }
            // write end extra text
            bout.Write(END_EXTRA_TEXT);
        }