/// <summary> /// Allocate a new counter with a given label and type. /// </summary> /// <param name="label"> to describe the counter. </param> /// <param name="typeId"> for the type of counter. </param> /// <returns> the id allocated for the counter. </returns> public int Allocate(string label, int typeId = DEFAULT_TYPE_ID) { int counterId = NextCounterId(); CheckCountersCapacity(counterId); int recordOffset = MetaDataOffset(counterId); CheckMetaDataCapacity(recordOffset); try { MetaDataBuffer.PutInt(recordOffset + TYPE_ID_OFFSET, typeId); MetaDataBuffer.PutLong(recordOffset + FREE_FOR_REUSE_DEADLINE_OFFSET, NOT_FREE_TO_REUSE); PutLabel(recordOffset, label); MetaDataBuffer.PutIntOrdered(recordOffset, RECORD_ALLOCATED); } catch (Exception) { _freeList.Add(counterId); throw; } return(counterId); }
/// <summary> /// Allocate a new counter with a given label. /// /// The key function will be called with a buffer with the exact length of available key space /// in the record for the user to store what they want for the key. No offset is required. /// </summary> /// <param name="label"> to describe the counter. </param> /// <param name="typeId"> for the type of counter. </param> /// <param name="keyFunc"> for setting the key value for the counter. </param> /// <returns> the id allocated for the counter. </returns> public int Allocate(string label, int typeId, Action <IMutableDirectBuffer> keyFunc) { var counterId = NextCounterId(); CheckCountersCapacity(counterId); var recordOffset = MetaDataOffset(counterId); CheckMetaDataCapacity(recordOffset); try { MetaDataBuffer.PutInt(recordOffset + TYPE_ID_OFFSET, typeId); keyFunc(new UnsafeBuffer(MetaDataBuffer, recordOffset + KEY_OFFSET, MAX_KEY_LENGTH)); MetaDataBuffer.PutLong(recordOffset + FREE_FOR_REUSE_DEADLINE_OFFSET, NOT_FREE_TO_REUSE); PutLabel(recordOffset, label); MetaDataBuffer.PutIntOrdered(recordOffset, RECORD_ALLOCATED); } catch (Exception) { _freeList.Add(counterId); throw; } return(counterId); }
/// <summary> /// Allocate a new counter with a given label. /// /// The key function will be called with a buffer with the exact length of available key space /// in the record for the user to store what they want for the key. No offset is required. /// </summary> /// <param name="label"> to describe the counter. </param> /// <param name="typeId"> for the type of counter. </param> /// <param name="keyFunc"> for setting the key value for the counter. </param> /// <returns> the id allocated for the counter. </returns> public int Allocate(string label, int typeId, Action <IMutableDirectBuffer> keyFunc) { var counterId = NextCounterId(); if (CounterOffset(counterId) + COUNTER_LENGTH > ValuesBuffer.Capacity) { throw new InvalidOperationException("Unable to allocated counter, values buffer is full"); } var recordOffset = MetaDataOffset(counterId); if (recordOffset + METADATA_LENGTH > MetaDataBuffer.Capacity) { throw new InvalidOperationException("Unable to allocate counter, labels buffer is full"); } try { MetaDataBuffer.PutInt(recordOffset + TYPE_ID_OFFSET, typeId); keyFunc(new UnsafeBuffer(MetaDataBuffer, recordOffset + KEY_OFFSET, MAX_KEY_LENGTH)); PutLabel(recordOffset, label); MetaDataBuffer.PutIntOrdered(recordOffset, RECORD_ALLOCATED); } catch (Exception) { _freeList.Enqueue(counterId); throw; } return(counterId); }
/// <summary> /// Allocate a new counter with a given label and type. /// </summary> /// <param name="label"> to describe the counter. </param> /// <param name="typeId"> for the type of counter. </param> /// <returns> the id allocated for the counter. </returns> public int Allocate(string label, int typeId = DEFAULT_TYPE_ID) { int counterId = NextCounterId(); if ((CounterOffset(counterId) + COUNTER_LENGTH) > ValuesBuffer.Capacity) { throw new InvalidOperationException("Unable to allocated counter, values buffer is full"); } int recordOffset = MetaDataOffset(counterId); if ((recordOffset + METADATA_LENGTH) > MetaDataBuffer.Capacity) { throw new InvalidOperationException("Unable to allocate counter, labels buffer is full"); } try { MetaDataBuffer.PutInt(recordOffset + TYPE_ID_OFFSET, typeId); PutLabel(recordOffset, label); MetaDataBuffer.PutIntOrdered(recordOffset, RECORD_ALLOCATED); } catch (Exception) { _freeList.Enqueue(counterId); throw; } return(counterId); }
/// <summary> /// Free the counter identified by counterId. /// </summary> /// <param name="counterId"> the counter to freed </param> public void Free(int counterId) { int recordOffset = MetaDataOffset(counterId); MetaDataBuffer.PutLong(recordOffset + FREE_FOR_REUSE_DEADLINE_OFFSET, _epochClock.Time() + _freeToReuseTimeoutMs); MetaDataBuffer.PutIntOrdered(recordOffset, RECORD_RECLAIMED); _freeList.Add(counterId); }
private string LabelValue(int recordOffset) { int labelLength = MetaDataBuffer.GetInt(recordOffset + LABEL_OFFSET); byte[] stringInBytes = new byte[labelLength]; MetaDataBuffer.GetBytes(recordOffset + LABEL_OFFSET + BitUtil.SIZE_OF_INT, stringInBytes); return(LabelCharset.GetString(stringInBytes)); }
private void PutLabel(int recordOffset, string label) { if (Encoding.ASCII.Equals(LabelCharset)) { int length = MetaDataBuffer.PutStringWithoutLengthAscii(recordOffset + LABEL_OFFSET + BitUtil.SIZE_OF_INT, label, 0, MAX_LABEL_LENGTH); MetaDataBuffer.PutInt(recordOffset + LABEL_OFFSET, length); } else { byte[] bytes = LabelCharset.GetBytes(label); int length = Math.Min(bytes.Length, MAX_LABEL_LENGTH); MetaDataBuffer.PutInt(recordOffset + LABEL_OFFSET, length); MetaDataBuffer.PutBytes(recordOffset + LABEL_OFFSET + BitUtil.SIZE_OF_INT, bytes, 0, length); } }
/// <summary> /// Allocate a counter with the minimum of allocation by allowing the label an key to be provided and copied. /// <para> /// If the keyBuffer is null then a copy of the key is not attempted. /// /// </para> /// </summary> /// <param name="typeId"> for the counter. </param> /// <param name="keyBuffer"> containing the optional key for the counter. </param> /// <param name="keyOffset"> within the keyBuffer at which the key begins. </param> /// <param name="keyLength"> of the key in the keyBuffer. </param> /// <param name="labelBuffer"> containing the mandatory label for the counter. </param> /// <param name="labelOffset"> within the labelBuffer at which the label begins. </param> /// <param name="labelLength"> of the label in the labelBuffer. </param> /// <returns> the id allocated for the counter. </returns> public int Allocate(int typeId, IDirectBuffer keyBuffer, int keyOffset, int keyLength, IDirectBuffer labelBuffer, int labelOffset, int labelLength) { int counterId = NextCounterId(); if ((CounterOffset(counterId) + COUNTER_LENGTH) > ValuesBuffer.Capacity) { throw new InvalidOperationException("Unable to allocated counter, values buffer is full"); } int recordOffset = MetaDataOffset(counterId); if ((recordOffset + METADATA_LENGTH) > MetaDataBuffer.Capacity) { throw new InvalidOperationException("Unable to allocate counter, labels buffer is full"); } try { MetaDataBuffer.PutInt(recordOffset + TYPE_ID_OFFSET, typeId); int length; if (null != keyBuffer) { length = Math.Min(keyLength, MAX_KEY_LENGTH); MetaDataBuffer.PutBytes(recordOffset + KEY_OFFSET, keyBuffer, keyOffset, length); } length = Math.Min(labelLength, MAX_LABEL_LENGTH); MetaDataBuffer.PutInt(recordOffset + LABEL_OFFSET, length); MetaDataBuffer.PutBytes(recordOffset + LABEL_OFFSET + BitUtil.SIZE_OF_INT, labelBuffer, labelOffset, length); MetaDataBuffer.PutIntOrdered(recordOffset, RECORD_ALLOCATED); } catch (Exception) { _freeList.Enqueue(counterId); throw; } return(counterId); }
/// <summary> /// Iterate over all labels in the label buffer. /// </summary> /// <param name="consumer"> function to be called for each label. </param> public void ForEach(IntObjConsumer <string> consumer) { var counterId = 0; for (int i = 0, capacity = MetaDataBuffer.Capacity; i < capacity; i += METADATA_LENGTH) { var recordStatus = MetaDataBuffer.GetIntVolatile(i); if (RECORD_UNUSED == recordStatus) { break; } else if (RECORD_ALLOCATED == recordStatus) { var label = MetaDataBuffer.GetStringUtf8(i + LABEL_OFFSET); consumer(counterId, label); } counterId++; } }
/// <summary> /// Iterate over the counters and provide the value and basic metadata. /// </summary> /// <param name="consumer"> for each allocated counter. </param> public void ForEach(CounterConsumer consumer) { int counterId = 0; for (int i = 0, capacity = MetaDataBuffer.Capacity; i < capacity; i += METADATA_LENGTH) { int recordStatus = MetaDataBuffer.GetIntVolatile(i); if (RECORD_ALLOCATED == recordStatus) { consumer(ValuesBuffer.GetLongVolatile(CounterOffset(counterId)), counterId, LabelValue(i)); } else if (RECORD_UNUSED == recordStatus) { break; } counterId++; } }
private int NextCounterId() { long nowMs = _epochClock.Time(); for (int i = 0, size = _freeList.Count; i < size; i++) { int counterId = _freeList[i]; long deadlineMs = MetaDataBuffer.GetLongVolatile(MetaDataOffset(counterId) + FREE_FOR_REUSE_DEADLINE_OFFSET); if (nowMs >= deadlineMs) { _freeList.Remove(i); ValuesBuffer.PutLongOrdered(CounterOffset(counterId), 0L); return(counterId); } } return(++_idHighWaterMark); }
private void PutLabel(int recordOffset, string label) { if (Encoding.ASCII.Equals(LabelCharset)) { MetaDataBuffer.PutStringAscii(recordOffset + LABEL_OFFSET, label.Length > MAX_LABEL_LENGTH ? label.Substring(0, MAX_LABEL_LENGTH) : label); } else { byte[] bytes = LabelCharset.GetBytes(label); if (bytes.Length > MAX_LABEL_LENGTH) { MetaDataBuffer.PutInt(recordOffset + LABEL_OFFSET, MAX_LABEL_LENGTH); MetaDataBuffer.PutBytes(recordOffset + LABEL_OFFSET + BitUtil.SIZE_OF_INT, bytes, 0, MAX_LABEL_LENGTH); } else { MetaDataBuffer.PutInt(recordOffset + LABEL_OFFSET, bytes.Length); MetaDataBuffer.PutBytes(recordOffset + LABEL_OFFSET + BitUtil.SIZE_OF_INT, bytes); } } }
/// <summary> /// Allocate a new counter with a given label. /// </summary> /// <param name="label"> to describe the counter. </param> /// <returns> the id allocated for the counter. </returns> public int Allocate(string label) { int counterId = NextCounterId(); if ((CounterOffset(counterId) + COUNTER_LENGTH) > ValuesBuffer.Capacity) { throw new ArgumentException("Unable to allocated counter, values buffer is full"); } int recordOffset = MetaDataOffset(counterId); if ((recordOffset + METADATA_LENGTH) > MetaDataBuffer.Capacity) { throw new ArgumentException("Unable to allocate counter, labels buffer is full"); } MetaDataBuffer.PutInt(recordOffset + TYPE_ID_OFFSET, DEFAULT_TYPE_ID); MetaDataBuffer.PutStringUtf8(recordOffset + LABEL_OFFSET, label, MAX_LABEL_LENGTH); MetaDataBuffer.PutIntOrdered(recordOffset, RECORD_ALLOCATED); return(counterId); }
/// <summary> /// Iterate over all the metadata in the buffer. /// </summary> /// <param name="metaData"> function to be called for each metadata record. </param> public void ForEach(MetaData metaData) { var counterId = 0; for (int i = 0, capacity = MetaDataBuffer.Capacity; i < capacity; i += METADATA_LENGTH) { var recordStatus = MetaDataBuffer.GetIntVolatile(i); if (RECORD_UNUSED == recordStatus) { break; } if (RECORD_ALLOCATED == recordStatus) { var typeId = MetaDataBuffer.GetInt(i + TYPE_ID_OFFSET); var label = MetaDataBuffer.GetStringUtf8(i + LABEL_OFFSET); IDirectBuffer keyBuffer = new UnsafeBuffer(MetaDataBuffer, i + KEY_OFFSET, MAX_KEY_LENGTH); metaData(counterId, typeId, keyBuffer, label); } counterId++; } }
/// <summary> /// Allocate a counter with the minimum of allocation by allowing the label an key to be provided and copied. /// <para> /// If the keyBuffer is null then a copy of the key is not attempted. /// /// </para> /// </summary> /// <param name="typeId"> for the counter. </param> /// <param name="keyBuffer"> containing the optional key for the counter. </param> /// <param name="keyOffset"> within the keyBuffer at which the key begins. </param> /// <param name="keyLength"> of the key in the keyBuffer. </param> /// <param name="labelBuffer"> containing the mandatory label for the counter. </param> /// <param name="labelOffset"> within the labelBuffer at which the label begins. </param> /// <param name="labelLength"> of the label in the labelBuffer. </param> /// <returns> the id allocated for the counter. </returns> public int Allocate(int typeId, IDirectBuffer keyBuffer, int keyOffset, int keyLength, IDirectBuffer labelBuffer, int labelOffset, int labelLength) { int counterId = NextCounterId(); CheckCountersCapacity(counterId); int recordOffset = MetaDataOffset(counterId); CheckMetaDataCapacity(recordOffset); try { MetaDataBuffer.PutInt(recordOffset + TYPE_ID_OFFSET, typeId); MetaDataBuffer.PutLong(recordOffset + FREE_FOR_REUSE_DEADLINE_OFFSET, NOT_FREE_TO_REUSE); int length; if (null != keyBuffer) { length = Math.Min(keyLength, MAX_KEY_LENGTH); MetaDataBuffer.PutBytes(recordOffset + KEY_OFFSET, keyBuffer, keyOffset, length); } length = Math.Min(labelLength, MAX_LABEL_LENGTH); MetaDataBuffer.PutInt(recordOffset + LABEL_OFFSET, length); MetaDataBuffer.PutBytes(recordOffset + LABEL_OFFSET + BitUtil.SIZE_OF_INT, labelBuffer, labelOffset, length); MetaDataBuffer.PutIntOrdered(recordOffset, RECORD_ALLOCATED); } catch (Exception) { _freeList.Add(counterId); throw; } return(counterId); }
/// <summary> /// Allocate a new counter with a given label. /// /// The key function will be called with a buffer with the exact length of available key space /// in the record for the user to store what they want for the key. No offset is required. /// </summary> /// <param name="label"> to describe the counter. </param> /// <param name="typeId"> for the type of counter. </param> /// <param name="keyFunc"> for setting the key value for the counter. </param> /// <returns> the id allocated for the counter. </returns> public int Allocate(string label, int typeId, Action <IMutableDirectBuffer> keyFunc) { var counterId = NextCounterId(); if (CounterOffset(counterId) + COUNTER_LENGTH > ValuesBuffer.Capacity) { throw new ArgumentException("Unable to allocated counter, values buffer is full"); } var recordOffset = MetaDataOffset(counterId); if (recordOffset + METADATA_LENGTH > MetaDataBuffer.Capacity) { throw new ArgumentException("Unable to allocate counter, labels buffer is full"); } MetaDataBuffer.PutInt(recordOffset + TYPE_ID_OFFSET, typeId); keyFunc(new UnsafeBuffer(MetaDataBuffer, recordOffset + KEY_OFFSET, MAX_KEY_LENGTH)); MetaDataBuffer.PutStringUtf8(recordOffset + LABEL_OFFSET, label, MAX_LABEL_LENGTH); MetaDataBuffer.PutIntOrdered(recordOffset, RECORD_ALLOCATED); return(counterId); }
/// <summary> /// Get the deadline (in milliseconds) for when a given counter id may be reused. /// </summary> /// <param name="counterId"> to be read. </param> /// <returns> deadline (in milliseconds) for when a given counter id may be reused or <seealso cref="NOT_FREE_TO_REUSE"/> if /// currently in use. </returns> public long GetFreeForReuseDeadline(int counterId) { ValidateCounterId(counterId); return(MetaDataBuffer.GetLongVolatile(MetaDataOffset(counterId) + FREE_FOR_REUSE_DEADLINE_OFFSET)); }
/// <summary> /// Free the counter identified by counterId. /// </summary> /// <param name="counterId"> the counter to freed </param> public void Free(int counterId) { MetaDataBuffer.PutIntOrdered(MetaDataOffset(counterId), RECORD_RECLAIMED); _freeList.Enqueue(counterId); }
/// <summary> /// Get the type id for a given counter id. /// </summary> /// <param name="counterId"> to be read. </param> /// <returns> the type id for a given counter id. </returns> public int GetCounterTypeId(int counterId) { ValidateCounterId(counterId); return(MetaDataBuffer.GetInt(MetaDataOffset(counterId) + TYPE_ID_OFFSET)); }
/// <summary> /// Get the state for a given counter id as a volatile read. /// </summary> /// <param name="counterId"> to be read. </param> /// <returns> the current state of the counter. </returns> /// <seealso cref="RECORD_UNUSED"></seealso> /// <seealso cref="RECORD_ALLOCATED"></seealso> /// <seealso cref="RECORD_RECLAIMED"></seealso> public int GetCounterState(int counterId) { ValidateCounterId(counterId); return(MetaDataBuffer.GetIntVolatile(MetaDataOffset(counterId))); }