public StringId CreateOrUpdateItem(StringId oldItem, bool hasOldItem, out bool remove) { remove = false; return(m_stringTable.AddStringToBuffer(m_sought)); }
/// <summary> /// Determines whether a string matches an entry in the table in an ordinally case-insensitive way. /// </summary> /// <remarks> /// This method is thread-safe. /// </remarks> internal bool CaseInsensitiveEquals(StringId id1, StringId id2) { Contract.Requires(id1.IsValid); Contract.Requires(id2.IsValid); Contract.Requires(IsValid()); if (id1 == id2) { return(true); } int index1 = id1.Value & BytesPerBufferMask; byte[] buffer1 = m_byteBuffers[(id1.Value >> BytesPerBufferBits) & NumByteBuffersMask]; int length1 = buffer1[index1++]; if (length1 == LongStringMarker) { Contract.Assume(index1 + 4 <= buffer1.Length); length1 = (buffer1[index1++] << 24) | (buffer1[index1++] << 16) | (buffer1[index1++] << 8) | (buffer1[index1++] << 0); } int index2 = id2.Value & BytesPerBufferMask; byte[] buffer2 = m_byteBuffers[(id2.Value >> BytesPerBufferBits) & NumByteBuffersMask]; int length2 = buffer2[index2++]; if (length2 == LongStringMarker) { Contract.Assume(index2 + 4 <= buffer2.Length); length2 = (buffer2[index2++] << 24) | (buffer2[index2++] << 16) | (buffer2[index2++] << 8) | (buffer2[index2++] << 0); } if (length1 != length2) { // different lengths return(false); } if (buffer1[index1] != Utf16Marker) { Contract.Assume((index1 + length1) <= buffer1.Length); Contract.Assume((index2 + length2) <= buffer2.Length); // characters are 8 bits for (int i = 0; i < length1; i++) { var ch1 = (char)buffer1[index1 + i]; var ch2 = (char)buffer2[index2 + i]; if (ch1.ToUpperInvariantFast() != ch2.ToUpperInvariantFast()) { return(false); } } } else { if (buffer2[index1] != Utf16Marker) { // different marker bytes return(false); } Contract.Assume((index1 + (length1 * 2) + 1) <= buffer1.Length); Contract.Assume((index2 + (length2 * 2) + 1) <= buffer2.Length); // characters are 16 bits index1++; // skip the marker index2++; // skip the marker for (int i = 0; i < length1; i++) { var ch1 = (char)((buffer1[index1 + (2 * i)] << 8) | buffer1[index1 + (2 * i) + 1]); var ch2 = (char)((buffer2[index2 + (2 * i)] << 8) | buffer2[index2 + (2 * i) + 1]); if (ch1.ToUpperInvariantFast() != ch2.ToUpperInvariantFast()) { return(false); } } } return(true); }
public bool Equals(StringId other) { return(m_stringTable.Equals(m_sought, other)); }
/// <summary> /// Writes a StringId /// </summary> public virtual void Write(StringId value) { Start <StringId>(); Write(value.Value); End(); }
internal int CopyString(StringId id, char[] destination, int destinationIndex, bool isEndIndex = false) { return(CopyString(id, ref destination, destinationIndex, isEndIndex: isEndIndex, allowResizeBuffer: false)); }
private StringId AddStringToBuffer <T>(T seg) where T : struct, ICharSpan <T> { Contract.Requires(IsValid()); Contract.Ensures(Contract.Result <StringId>().IsValid); // Not in the set, so get some space to hold the string // // Note that this call is invoked in a racy setting. It's possible for multiple calls to execute concurrently and // try to insert the same string N times. We allow this and end up just wasting space in the table when this happens. // In this context, m_count thus accounts for the total number of strings inserted into the table, and not the potential // smaller of logical strings known to the table. bool longString = seg.Length >= 255; int space = 1; if (longString) { // if the length >= 255 then we store it as a marker byte followed by a 4-byte length value space += 4; } // see if the string is pure ASCII bool isAscii = seg.OnlyContains8BitChars; if (isAscii) { // stored as bytes space += seg.Length; } else { // stored as UTF-16 space += (seg.Length * 2) + 1; // *2 for UTF-16, +1 for the 'switch to unicode' marker byte } // count how many strings are in the buffer Interlocked.Increment(ref m_count); int byteIndex; int bufferNum; // loop until we find a suitable location to copy the string to while (true) { // get the next possible location for the string int current = Volatile.Read(ref m_nextId); bufferNum = (current >> BytesPerBufferBits) & NumByteBuffersMask; byteIndex = current & BytesPerBufferMask; // is the available space big enough? if (space < BytesPerBuffer - byteIndex) { // there's room in the buffer so try to claim it int next = (bufferNum << BytesPerBufferBits) | (byteIndex + space); if (Interlocked.CompareExchange(ref m_nextId, next, current) == current) { // got some room, now go fill it in break; } // go try again... continue; } // the string doesn't fit in the current buffer, we need a new buffer int newBufferSize = BytesPerBuffer; if (space > BytesPerBuffer) { newBufferSize = space; } bufferNum++; // Make sure we don't overflow the buffer we're indexing into if (bufferNum >= NumByteBuffers) { Contract.Assert(false, $"Exceeded the number of ByteBuffers allowed in this StringTable: {bufferNum} >= {NumByteBuffers}"); } lock (m_byteBuffers) { if (m_byteBuffers[bufferNum] != null) { // somebody racily beat us and allocated this buffer, so just retry the whole thing from scratch continue; } // allocate a new buffer m_byteBuffers[bufferNum] = new byte[newBufferSize]; if (space >= BytesPerBuffer) { // force writing into a fresh buffer Volatile.Write(ref m_nextId, (bufferNum << BytesPerBufferBits) | BytesPerBufferMask); } else { // force writing into this new buffer Volatile.Write(ref m_nextId, (bufferNum << BytesPerBufferBits) | space); } // go write the string at the base of the new buffer byteIndex = 0; break; } } #if DebugStringTable var stringId = new StringId((bufferNum << BytesPerBufferBits) + byteIndex, m_debugIndex); #else var stringId = new StringId((bufferNum << BytesPerBufferBits) + byteIndex); #endif Contract.Assert(stringId.IsValid); // now copy the string data into the buffer byte[] currentBuffer = m_byteBuffers[bufferNum]; if (longString) { currentBuffer[byteIndex++] = LongStringMarker; Bits.WriteInt32(currentBuffer, ref byteIndex, seg.Length); } else { currentBuffer[byteIndex++] = (byte)seg.Length; } if (isAscii) { seg.CopyAs8Bit(currentBuffer, byteIndex); } else { currentBuffer[byteIndex++] = Utf16Marker; seg.CopyAs16Bit(currentBuffer, byteIndex); } return(stringId); }
internal static int GetHashCode(StringId id) { return(id.Value); }