GetDateCollectionMaximumLength

Retrieve the maximum capacity of a date collection

Overview

GetDateCollectionMaximumLength returns the maximum capacity (allocation limit) of a date collection handle. This function provides thread-safe access to the collection's maximum length by acquiring the internal mutex and reading the maximum length field.

Purpose

Get the maximum number of date items that can be stored in a collection. This is useful for:

Key Features

Function Signature


DWORD _Check_return_ _On_failure_(return > DATE_LOG_MAX) GetDateCollectionMaximumLength(
    _In_ DATE_COLLECTION_HANDLE hDateCollection
);
            

Calling Convention

Standard C calling convention with SAL annotations for parameter and return value validation.

SAL Annotations


_Check_return_ : Caller must check the return value; ignoring it may lead to resource misallocation
_On_failure_(return > DATE_LOG_MAX) : On failure, return value will exceed DATE_LOG_MAX (range 0x000000 to 0x001388)
                

Parameters

hDateCollection

[In] DATE_COLLECTION_HANDLE

A handle to a date collection previously created via one of the initialization functions.

  • Must be a valid DATE_COLLECTION_HANDLE (DATE_LOG_HANDLE, DATE_RANGE_HANDLE, or HOLIDAYS_HANDLE)
  • Must not be NULL
  • Must have been created and not yet closed/freed
  • Precondition: Must be pre-validated by IsValidDateLogHandle()

Return Values

Success Case

1 to DATE_LOG_MAX (0x00000001 to 0x00001388)

Returns the maximum capacity of the collection. Range is 1 through DATE_LOG_MAX (5000 items maximum). The returned value is always >= 1 because collections cannot be created with zero capacity.

Error Case

DATE_LOG_SENTINEL (0x00845698)

Indicates an error occurred. Possible error conditions:

Return Value Semantics

Return Value Meaning Action
0x00000001 to 0x001388 Maximum capacity (valid) Normal operation; collection can hold up to this many items
0x00000000 Unexpected zero capacity Data corruption; handle state is invalid
0x00845698 ERROR_SENTINEL (error) Do not use returned value; close handle and retry
Any value > 0x001388 Data corruption detected Collection exceeds maximum allowed; handle state is unreliable

Constant Reference

DATE_LOG_MAX = 0x00001388 (5000 in decimal) DATE_LOG_SENTINEL = 0x00845698 (8821784 in decimal)

Checking Return Values


// Pattern 1: Simple validity check
DWORD dwMaxLength = GetDateCollectionMaximumLength(hCollection);
if (dwMaxLength == DATE_LOG_SENTINEL) {
    // Error occurred
    printf("Failed to get collection maximum length\n");
    // Close handle and retry
} else if (dwMaxLength == 0 || dwMaxLength > DATE_LOG_MAX) {
    // Data corruption detected
    printf("ERROR: Invalid maximum length: %u\n", dwMaxLength);
} else {
    // Valid maximum capacity
    printf("Collection capacity: %u items\n", dwMaxLength);
}

// Pattern 2: Calculate utilization
DWORD dwCurrentLen = GetDateCollectionLength(hCollection);
DWORD dwMaxLen = GetDateCollectionMaximumLength(hCollection);

if (dwCurrentLen != DATE_LOG_SENTINEL && dwMaxLen != DATE_LOG_SENTINEL) {
    double utilization = (100.0 * dwCurrentLen) / dwMaxLen;
    printf("Utilization: %.1f%% (%u of %u items)\n", 
        utilization, dwCurrentLen, dwMaxLen);
    
    if (utilization > 90.0) {
        printf("WARNING: Collection nearly full!\n");
    }
}

// Pattern 3: Check available capacity
DWORD dwRemaining = dwMaxLen - dwCurrentLen;
printf("Remaining capacity: %u items\n", dwRemaining);
            

Behavior and Operations

Execution Flow

Validate Handle
Acquire Mutex
Read Maximum
Release Mutex

Step-by-Step Operation

  1. Handle Validation:
    Calls IsValidDateLogHandle(hDateCollection) to verify:
    • Handle pointer is not NULL
    • Handle magic number is valid (not marked invalid)
    • Handle context is not NULL
    • Handle type matches expected enum value
  2. Early Exit on Invalid Handle:
    If validation fails, immediately returns DATE_LOG_SENTINEL
  3. Context Extraction:
    Casts handle to PDATE_LOG and retrieves the core handle from pContext->hDateLogCore
  4. Delegate to Core Getter:
    Calls GetDateLogCoreMaxLength() which:
    • Re-validates the core pointer
    • Acquires the core's mutex with 5-second timeout
    • Reads the dwMaxLength field inside the mutex
    • Releases the mutex
  5. Return Result:
    Returns the maximum length value or DATE_LOG_SENTINEL if any step fails

Value Immutability

The maximum length of a collection is established at initialization and never changes:

Thread Safety

The function is fully thread-safe:

Comparison with GetDateCollectionLength

Aspect GetDateCollectionLength GetDateCollectionMaximumLength
Reads Field dwLength dwMaxLength
Changes Over Time Yes (0 to max as items added) No (fixed at initialization)
Typical Use Get current item count Get capacity limit
Can Be Zero Yes (empty collection) No (always >= 1)

Usage Examples

Example 1: Check Capacity Before Adding

#include "datelog.h"

DWORD dwCurrentLength = GetDateCollectionLength(hMyCollection);
DWORD dwMaxLength = GetDateCollectionMaximumLength(hMyCollection);

if (dwCurrentLength == DATE_LOG_SENTINEL || dwMaxLength == DATE_LOG_SENTINEL) {
    printf("ERROR: Failed to get collection information\n");
    goto error_cleanup;
}

DWORD dwRemaining = dwMaxLength - dwCurrentLength;

if (dwRemaining < 100) {
    printf("WARNING: Only %u items of capacity remaining\n", dwRemaining);
    printf("Collection is %.1f%% full\n", 
        (100.0 * dwCurrentLength) / dwMaxLength);
} else {
    printf("Plenty of space available (%u items)\n", dwRemaining);
}

// ... continue with operation ...

error_cleanup:
    CloseDateLogHandle(hMyCollection);
    return FALSE;
                
Example 2: Pre-allocate Buffer Based on Maximum Capacity

// Allocate buffer sized to collection's maximum capacity
DWORD dwMaxLength = GetDateCollectionMaximumLength(hCollection);

if (dwMaxLength == DATE_LOG_SENTINEL || dwMaxLength == 0) {
    printf("Cannot allocate buffer for invalid collection\n");
    return NULL;
}

if (dwMaxLength > DATE_LOG_MAX) {
    printf("ERROR: Collection capacity exceeds maximum allowed\n");
    return NULL;
}

// Each date item is sizeof(DATE_STRUCT) bytes
SIZE_T cbBuffer = (SIZE_T)dwMaxLength * sizeof(DATE_STRUCT);

// Check for integer overflow
if (cbBuffer / sizeof(DATE_STRUCT) != dwMaxLength) {
    printf("ERROR: Buffer size calculation overflow\n");
    return NULL;
}

PDATE_STRUCT pDates = (PDATE_STRUCT)malloc(cbBuffer);
if (pDates == NULL) {
    printf("Failed to allocate %zu bytes\n", cbBuffer);
    return NULL;
}

printf("Allocated %zu bytes for maximum %u dates\n", cbBuffer, dwMaxLength);
return pDates;
                
Example 3: Calculate Utilization After Operations

void PrintCollectionStatistics(DATE_COLLECTION_HANDLE hCollection) {
    DWORD dwLength = GetDateCollectionLength(hCollection);
    DWORD dwMaxLength = GetDateCollectionMaximumLength(hCollection);
    
    if (dwLength == DATE_LOG_SENTINEL || dwMaxLength == DATE_LOG_SENTINEL) {
        printf("Collection is invalid or inaccessible\n");
        return;
    }
    
    // Validate data integrity
    if (dwLength > dwMaxLength) {
        printf("ERROR: Current length (%u) exceeds maximum (%u)\n", 
            dwLength, dwMaxLength);
        return;
    }
    
    // Calculate statistics
    DWORD dwRemaining = dwMaxLength - dwLength;
    double percentUsed = (100.0 * dwLength) / dwMaxLength;
    double percentFree = 100.0 - percentUsed;
    
    printf("=== Collection Statistics ===\n");
    printf("Current items:    %u\n", dwLength);
    printf("Maximum capacity: %u\n", dwMaxLength);
    printf("Remaining space:  %u items\n", dwRemaining);
    printf("Utilization:      %.1f%% used, %.1f%% free\n", 
        percentUsed, percentFree);
    
    // Alert on capacity thresholds
    if (percentUsed >= 90.0) {
        printf("⚠ CRITICAL: Collection is nearly full!\n");
    } else if (percentUsed >= 75.0) {
        printf("⚠ WARNING: Collection is getting full\n");
    } else if (percentUsed >= 50.0) {
        printf("ℹ INFO: Collection is half-full\n");
    }
}
                
Example 4: Validate Merge Won't Exceed Capacity

// Before merging, check if result will fit
DWORD dwDestCurrent = GetDateCollectionLength(hDestLog);
DWORD dwDestMax = GetDateCollectionMaximumLength(hDestLog);
DWORD dwSourceLength = GetDateCollectionLength(hSourceLog);

if (dwDestCurrent == DATE_LOG_SENTINEL || 
    dwDestMax == DATE_LOG_SENTINEL || 
    dwSourceLength == DATE_LOG_SENTINEL) {
    printf("ERROR: Cannot get collection sizes\n");
    return FALSE;
}

// Estimate space needed (conservative: assume all source items are new)
DWORD dwEstimatedTotal = dwDestCurrent + dwSourceLength;

if (dwEstimatedTotal > dwDestMax) {
    printf("ERROR: Merge would exceed capacity\n");
    printf("Destination: %u current + %u source = %u total\n", 
        dwDestCurrent, dwSourceLength, dwEstimatedTotal);
    printf("Maximum capacity: %u\n", dwDestMax);
    printf("Shortfall: %u items\n", dwEstimatedTotal - dwDestMax);
    return FALSE;
} else {
    DWORD dwRemaining = dwDestMax - dwEstimatedTotal;
    printf("Merge will fit with %u items to spare\n", dwRemaining);
    
    // Proceed with merge
    return SUCCEEDED(MergeDateLogHandle(hDestLog, hSourceLog, 
                                        DATE_RANGE_MERGE_FAVOR_SOURCE));
}
                

Thread Safety and Synchronization

Mutex Acquisition

Aspect Detail
Mutex Timeout 5 seconds (5000 milliseconds)
Lock Scope Only the dwMaxLength field read
Lock Duration Minimal (~1-2 microseconds)
Deadlock Risk None—single lock acquisition
Value Consistency Always consistent; field is immutable

Concurrent Access Scenarios

Scenario 1: Multiple Concurrent Reads
Multiple threads calling GetDateCollectionMaximumLength simultaneously will each acquire and release the mutex independently. Since the value is immutable, all threads will always see the same value.
Scenario 2: Read During Collection Modification
While MergeDateLogHandle or similar function modifies the collection (changing dwLength), concurrent calls to GetDateCollectionMaximumLength will read the unchanging dwMaxLength field and return the same value unaffected.
Scenario 3: Timeout During High Contention
If high thread contention causes mutex wait to exceed 5 seconds, the function returns DATE_LOG_SENTINEL rather than blocking indefinitely. Caller should handle this gracefully.

Visibility Guarantees

Optimization Opportunity

Value Caching:
Because dwMaxLength never changes, callers may cache the result and reuse it instead of repeatedly calling this function. Example:
DWORD dwMaxCapacity = GetDateCollectionMaximumLength(hCollection);
(Call once at initialization, reuse throughout program lifetime)

Important Notes and Considerations

Return Value Semantics

Maximum Length Always >= 1:
The maximum length is never 0. Collections cannot be created with zero capacity. If you receive 0, it indicates data corruption.
Sentinel vs. Error:
Always check return value == DATE_LOG_SENTINEL (0x00845698) to detect errors. A returned value of 0 indicates corruption, not an empty collection.

Immutability of Maximum Capacity

Related Functions

Performance Considerations

Common Pitfalls

Relationship to DATE_LOG_MAX Constant

System Maximum: DATE_LOG_MAX (0x00001388 = 5000) is the highest value that GetDateCollectionMaximumLength can return. Collections created with capacity > DATE_LOG_MAX will be rejected during initialization. If this function returns a value > DATE_LOG_MAX, it indicates data corruption and the handle should be closed immediately.

Updates and Changes