text:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Azure;
using Azure.Data.Tables;
using Microsoft.Extensions.Logging;
using Orleans.Internal;
using Orleans.Runtime;
using LogLevel = Microsoft.Extensions.Logging.LogLevel;

//
// Number of #ifs can be reduced (or removed), once we separate test projects by feature/area, otherwise we are ending up with ambigous types and build errors.
//

#if ORLEANS_CLUSTERING
namespace Orleans.Clustering.AzureStorage
#elif ORLEANS_PERSISTENCE
namespace Orleans.Persistence.AzureStorage
#elif ORLEANS_REMINDERS
namespace Orleans.Reminders.AzureStorage
#elif ORLEANS_STREAMING
namespace Orleans.Streaming.AzureStorage
#elif ORLEANS_EVENTHUBS
namespace Orleans.Streaming.EventHubs
#elif TESTER_AZUREUTILS
namespace Orleans.Tests.AzureUtils
#elif ORLEANS_TRANSACTIONS
namespace Orleans.Transactions.AzureStorage
#elif ORLEANS_DIRECTORY
namespace Orleans.GrainDirectory.AzureStorage
#else
// No default namespace intentionally to cause compile errors if something is not defined
#endif
{
    /// <summary>
    /// Utility class to encapsulate row-based access to Azure table storage.
    /// </summary>
    /// <typeparam name="T">Table data entry used by this table / manager.</typeparam>
    internal class AzureTableDataManager<T> where T : class, ITableEntity, new()
    {
        private readonly AzureStorageOperationOptions options;

        /// <summary> Name of the table this instance is managing. </summary>
        public string TableName { get; }

        /// <summary> Logger for this table manager instance. </summary>
        protected internal ILogger Logger { get; }

        public AzureStoragePolicyOptions StoragePolicyOptions { get; }

        public TableClient Table { get; private set; }

        /// <summary>
        /// Creates a new <see cref="AzureTableDataManager{T}"/> instance.
        /// </summary>
        /// <param name="options">Storage configuration.</param>
        /// <param name="logger">Logger to use.</param>
        public AzureTableDataManager(AzureStorageOperationOptions options, ILogger logger)
        {
            this.options = options ?? throw new ArgumentNullException(nameof(options));

            Logger = logger ?? throw new ArgumentNullException(nameof(logger));
            TableName = options.TableName ?? throw new ArgumentNullException(nameof(options.TableName));
            StoragePolicyOptions = options.StoragePolicyOptions ?? throw new ArgumentNullException(nameof(options.StoragePolicyOptions));

            AzureTableUtils.ValidateTableName(TableName);
        }

        /// <summary>
        /// Connects to, or creates and initializes a new Azure table if it does not already exist.
        /// </summary>
        /// <returns>Completion promise for this operation.</returns>
        public async Task InitTableAsync()
        {
            const string operation = "InitTable";
            var startTime = DateTime.UtcNow;

            try
            {
                TableServiceClient tableCreationClient = await GetCloudTableCreationClientAsync();
                var table = tableCreationClient.GetTableClient(TableName);
                var tableItem = await table.CreateIfNotExistsAsync();
                var didCreate = tableItem is not null;

                Logger.LogInformation((int)Utilities.ErrorCode.AzureTable_01, "{Action} Azure storage table {TableName}", (didCreate ? "Created" : "Attached to"), TableName);

                Table = table;
            }
            catch (TimeoutException te)
            {
                Logger.LogError(
                    (int)Utilities.ErrorCode.AzureTable_TableNotCreated,
                    te,
                    "Unable to create or connect to the Azure table in {CreationTimeout}",
                    StoragePolicyOptions.CreationTimeout);
                throw new OrleansException($"Unable to create or connect to the Azure table in {StoragePolicyOptions.CreationTimeout}", te);
            }
            catch (Exception exc)
            {
                Logger.LogError((int)Utilities.ErrorCode.AzureTable_02, exc, "Could not initialize connection to storage table {TableName}", TableName);
                throw;
            }
            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }

        /// <summary>
        /// Deletes the Azure table.
        /// </summary>
        /// <returns>Completion promise for this operation.</returns>
        public async Task DeleteTableAsync()
        {
            const string operation = "DeleteTable";
            var startTime = DateTime.UtcNow;

            try
            {
                var tableCreationClient = await GetCloudTableCreationClientAsync();
                var response = await tableCreationClient.DeleteTableAsync(TableName);
                if (response.Status == 204)
                {
                    Logger.LogInformation((int)Utilities.ErrorCode.AzureTable_03, "Deleted Azure storage table {TableName}", TableName);
                }
            }
            catch (Exception exc)
            {
                Logger.LogError((int)Utilities.ErrorCode.AzureTable_04, exc, "Could not delete storage table {TableName}", TableName);
                throw;
            }
            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }

        /// <summary>
        /// Deletes all entities the Azure table.
        /// </summary>
        /// <returns>Completion promise for this operation.</returns>
        public async Task ClearTableAsync()
        {
            var items = await ReadAllTableEntriesAsync();
            IEnumerable<Task> work = items.GroupBy(item => item.Item1.PartitionKey)
                                          .SelectMany(partition => partition.ToBatch(this.StoragePolicyOptions.MaxBulkUpdateRows))
                                          .Select(batch => DeleteTableEntriesAsync(batch.ToList()));
            await Task.WhenAll(work);
        }

        /// <summary>
        /// Create a new data entry in the Azure table (insert new, not update existing).
        /// Fails if the data already exists.
        /// </summary>
        /// <param name="data">Data to be inserted into the table.</param>
        /// <returns>Value promise with new Etag for this data entry after completing this storage operation.</returns>
        public async Task<string> CreateTableEntryAsync(T data)
        {
            const string operation = "CreateTableEntry";
            var startTime = DateTime.UtcNow;

            if (Logger.IsEnabled(LogLevel.Trace)) Logger.LogTrace("Creating {TableName} table entry: {Data}", TableName, data);

            try
            {
                try
                {
                    // Presumably FromAsync(BeginExecute, EndExecute) has a slightly better performance then CreateIfNotExistsAsync.
                    var opResult = await Table.AddEntityAsync(data);
                    return opResult.Headers.ETag.GetValueOrDefault().ToString();
                }
                catch (Exception exc)
                {
                    CheckAlertWriteError(operation, data, null, exc);
                    throw;
                }
            }
            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }

        /// <summary>
        /// Inserts a data entry in the Azure table: creates a new one if does not exists or overwrites (without eTag) an already existing version (the "update in place" semantics).
        /// </summary>
        /// <param name="data">Data to be inserted or replaced in the table.</param>
        /// <returns>Value promise with new Etag for this data entry after completing this storage operation.</returns>
        public async Task<string> UpsertTableEntryAsync(T data)
        {
            const string operation = "UpsertTableEntry";
            var startTime = DateTime.UtcNow;
            if (Logger.IsEnabled(LogLevel.Trace)) Logger.LogTrace("{Operation} entry {Data} into table {TableName}", operation, data, TableName);

            try
            {
                try
                {
                    var opResult = await Table.UpsertEntityAsync(data);
                    return opResult.Headers.ETag.GetValueOrDefault().ToString();
                }
                catch (Exception exc)
                {
                    Logger.LogWarning((int)Utilities.ErrorCode.AzureTable_06, exc,
                        "Intermediate error upserting entry {Data} to the table {TableName}", data == null ? "null" : data.ToString(), TableName);
                    throw;
                }
            }
            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }

        /// <summary>
        /// Inserts a data entry in the Azure table: creates a new one if does not exists
        /// </summary>
        /// <param name="data">Data to be inserted or replaced in the table.</param>
        /// <returns>Value promise with new Etag for this data entry after completing this storage operation.</returns>
        public async Task<(bool isSuccess, string eTag)> InsertTableEntryAsync(T data)
        {
            const string operation = "InsertTableEntry";
            var startTime = DateTime.UtcNow;
            if (Logger.IsEnabled(LogLevel.Trace)) Logger.LogTrace("{Operation} entry {Data} into table {TableName}", operation, data, TableName);

            try
            {
                try
                {
                    var opResult = await Table.AddEntityAsync(data);
                    return (true, opResult.Headers.ETag.GetValueOrDefault().ToString());
                }
                catch (RequestFailedException storageException) when (storageException.Status == (int)HttpStatusCode.Conflict)
                {
                    return (false, null);
                }
                catch (Exception exc)
                {
                    Logger.LogWarning((int)Utilities.ErrorCode.AzureTable_06, exc,
                        "Intermediate error inserting entry {Data} to the table {TableName}", data == null ? "null" : data.ToString(), TableName);
                    throw;
                }
            }
            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }

        /// <summary>
        /// Merges a data entry in the Azure table.
        /// </summary>
        /// <param name="data">Data to be merged in the table.</param>
        /// <param name="eTag">ETag to apply.</param>
        /// <returns>Value promise with new Etag for this data entry after completing this storage operation.</returns>
        internal Task<string> MergeTableEntryAsync(T data, string eTag) => MergeTableEntryAsync(data, new ETag(eTag));

        /// <summary>
        /// Merges a data entry in the Azure table.
        /// </summary>
        /// <param name="data">Data to be merged in the table.</param>
        /// <param name="eTag">ETag to apply.</param>
        /// <returns>Value promise with new Etag for this data entry after completing this storage operation.</returns>
        internal async Task<string> MergeTableEntryAsync(T data, ETag eTag)
        {
            const string operation = "MergeTableEntry";
            var startTime = DateTime.UtcNow;
            if (Logger.IsEnabled(LogLevel.Trace)) Logger.LogTrace("{Operation} entry {Data} into table {TableName}", operation, data, TableName);

            try
            {

                try
                {
                    // Merge requires an ETag (which may be the '*' wildcard).
                    data.ETag = eTag;
                    var opResult = await Table.UpdateEntityAsync(data, data.ETag, TableUpdateMode.Merge);
                    return opResult.Headers.ETag.GetValueOrDefault().ToString();
                }
                catch (Exception exc)
                {
                    Logger.LogWarning((int)Utilities.ErrorCode.AzureTable_07, exc,
                        "Intermediate error merging entry {Data} to the table {TableName}", data == null ? "null" : data.ToString(), TableName);
                    throw;
                }
            }
            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }

        /// <summary>
        /// Updates a data entry in the Azure table: updates an already existing data in the table, by using eTag.
        /// Fails if the data does not already exist or of eTag does not match.
        /// </summary>
        /// <param name="data">Data to be updated into the table.</param>
        /// /// <param name="dataEtag">ETag to use.</param>
        /// <returns>Value promise with new Etag for this data entry after completing this storage operation.</returns>
        public Task<string> UpdateTableEntryAsync(T data, string dataEtag) => UpdateTableEntryAsync(data, new ETag(dataEtag));

        /// <summary>
        /// Updates a data entry in the Azure table: updates an already existing data in the table, by using eTag.
        /// Fails if the data does not already exist or of eTag does not match.
        /// </summary>
        /// <param name="data">Data to be updated into the table.</param>
        /// /// <param name="dataEtag">ETag to use.</param>
        /// <returns>Value promise with new Etag for this data entry after completing this storage operation.</returns>
        public async Task<string> UpdateTableEntryAsync(T data, ETag dataEtag)
        {
            const string operation = "UpdateTableEntryAsync";
            var startTime = DateTime.UtcNow;
            if (Logger.IsEnabled(LogLevel.Trace)) Logger.LogTrace("{Operation} entry {Data} into table {TableName}", operation, data, TableName);

            try
            {
                try
                {
                    data.ETag = dataEtag;
                    var opResult = await Table.UpdateEntityAsync(data, data.ETag, TableUpdateMode.Replace);

                    //The ETag of data is needed in further operations.
                    return opResult.Headers.ETag.GetValueOrDefault().ToString();
                }
                catch (Exception exc)
                {
                    CheckAlertWriteError(operation, data, null, exc);
                    throw;
                }
            }
            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }

        /// <summary>
        /// Deletes an already existing data in the table, by using eTag.
        /// Fails if the data does not already exist or if eTag does not match.
        /// </summary>
        /// <param name="data">Data entry to be deleted from the table.</param>
        /// <param name="eTag">ETag to use.</param>
        /// <returns>Completion promise for this storage operation.</returns>
        public Task DeleteTableEntryAsync(T data, string eTag) => DeleteTableEntryAsync(data, new ETag(eTag));

        /// <summary>
        /// Deletes an already existing data in the table, by using eTag.
        /// Fails if the data does not already exist or if eTag does not match.
        /// </summary>
        /// <param name="data">Data entry to be deleted from the table.</param>
        /// <param name="eTag">ETag to use.</param>
        /// <returns>Completion promise for this storage operation.</returns>
        public async Task DeleteTableEntryAsync(T data, ETag eTag)
        {
            const string operation = "DeleteTableEntryAsync";
            var startTime = DateTime.UtcNow;
            if (Logger.IsEnabled(LogLevel.Trace)) Logger.LogTrace("{Operation} entry {Data} table {TableName}", operation, data, TableName);

            try
            {
                data.ETag = eTag;

                try
                {
                    var response = await Table.DeleteEntityAsync(data.PartitionKey, data.RowKey, data.ETag);
                    if (response is { Status: 404 })
                    {
                        throw new RequestFailedException(response.Status, "Resource not found", response.ReasonPhrase, null);
                    }
                }
                catch (Exception exc)
                {
                    Logger.LogWarning((int)Utilities.ErrorCode.AzureTable_08, exc,
                        "Intermediate error deleting entry {Data} from the table {TableName}.", data, TableName);
                    throw;
                }
            }
            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }

        /// <summary>
        /// Read a single table entry from the storage table.
        /// </summary>
        /// <param name="partitionKey">The partition key for the entry.</param>
        /// <param name="rowKey">The row key for the entry.</param>
        /// <returns>Value promise for tuple containing the data entry and its corresponding etag.</returns>
        public async Task<(T Entity, string ETag)> ReadSingleTableEntryAsync(string partitionKey, string rowKey)
        {
            const string operation = "ReadSingleTableEntryAsync";
            var startTime = DateTime.UtcNow;
            if (Logger.IsEnabled(LogLevel.Trace)) Logger.LogTrace("{Operation} table {TableName} partitionKey {PartitionKey} rowKey {RowKey}", operation, TableName, partitionKey, rowKey);
            T retrievedResult = default(T);

            try
            {
                try
                {
                    retrievedResult = await Table.GetEntityAsync<T>(partitionKey, rowKey);
                }
                catch (RequestFailedException exception)
                {
                    if (!AzureTableUtils.TableStorageDataNotFound(exception))
                    {
                        throw;
                    }
                }

                //The ETag of data is needed in further operations.
                if (retrievedResult != null) return (retrievedResult, retrievedResult.ETag.ToString());
                if (Logger.IsEnabled(LogLevel.Debug)) Logger.LogDebug("Could not find table entry for PartitionKey={PartitionKey} RowKey={RowKey}", partitionKey, rowKey);
                return (default, default);  // No data
            }
            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }

        /// <summary>
        /// Read all entries in one partition of the storage table.
        /// NOTE: This could be an expensive and slow operation for large table partitions!
        /// </summary>
        /// <param name="partitionKey">The key for the partition to be searched.</param>
        /// <returns>Enumeration of all entries in the specified table partition.</returns>
        public Task<List<(T Entity, string ETag)>> ReadAllTableEntriesForPartitionAsync(string partitionKey)
        {
            string query = TableClient.CreateQueryFilter($"PartitionKey eq {partitionKey}");
            return ReadTableEntriesAndEtagsAsync(query);
        }

        /// <summary>
        /// Read all entries in the table.
        /// NOTE: This could be a very expensive and slow operation for large tables!
        /// </summary>
        /// <returns>Enumeration of all entries in the table.</returns>
        public Task<List<(T Entity, string ETag)>> ReadAllTableEntriesAsync()
        {
            return ReadTableEntriesAndEtagsAsync(null);
        }

        /// <summary>
        /// Deletes a set of already existing data entries in the table, by using eTag.
        /// Fails if the data does not already exist or if eTag does not match.
        /// </summary>
        /// <param name="collection">Data entries and their corresponding etags to be deleted from the table.</param>
        /// <returns>Completion promise for this storage operation.</returns>
        public async Task DeleteTableEntriesAsync(List<(T Entity, string ETag)> collection)
        {
            const string operation = "DeleteTableEntries";
            var startTime = DateTime.UtcNow;
            if (Logger.IsEnabled(LogLevel.Trace)) Logger.LogTrace("{Operation} entries: {Data} table {TableName}", operation, Utils.EnumerableToString(collection), TableName);

            if (collection == null) throw new ArgumentNullException("collection");

            if (collection.Count > this.StoragePolicyOptions.MaxBulkUpdateRows)
            {
                throw new ArgumentOutOfRangeException("collection", collection.Count,
                        "Too many rows for bulk delete - max " + this.StoragePolicyOptions.MaxBulkUpdateRows);
            }

            if (collection.Count == 0)
            {
                return;
            }

            try
            {
                var entityBatch = new List<TableTransactionAction>();
                foreach (var tuple in collection)
                {
                    T item = tuple.Item1;
                    item.ETag = new ETag(tuple.Item2);
                    entityBatch.Add(new TableTransactionAction(TableTransactionActionType.Delete, item, item.ETag));
                }

                try
                {
                    _ = await Table.SubmitTransactionAsync(entityBatch);
                }
                catch (Exception exc)
                {
                    Logger.LogWarning((int)Utilities.ErrorCode.AzureTable_08, exc,
                        "Intermediate error deleting entries {Data} from the table {TableName}.", Utils.EnumerableToString(collection), TableName);
                    throw;
                }
            }
            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }

        /// <summary>
        /// Read data entries and their corresponding eTags from the Azure table.
        /// </summary>
        /// <param name="filter">Filter string to use for querying the table and filtering the results.</param>
        /// <returns>Enumeration of entries in the table which match the query condition.</returns>
        public async Task<List<(T Entity, string ETag)>> ReadTableEntriesAndEtagsAsync(string filter)
        {
            const string operation = "ReadTableEntriesAndEtags";
            var startTime = DateTime.UtcNow;

            try
            {

                try
                {
                    Func<Task<List<(T Entity, string ETag)>>> executeQueryHandleContinuations = async () =>
                    {
                        var list = new List<(T, string)>();
                        var results = Table.QueryAsync<T>(filter);
                        await foreach (var value in results)
                        {
                            list.Add((value, value.ETag.ToString()));
                        }

                        return list;
                    };

#if !ORLEANS_TRANSACTIONS
                    IBackoffProvider backoff = new FixedBackoff(this.StoragePolicyOptions.PauseBetweenOperationRetries);

                    List<(T, string)> results = await AsyncExecutorWithRetries.ExecuteWithRetries(
                        counter => executeQueryHandleContinuations(),
                        this.StoragePolicyOptions.MaxOperationRetries,
                        (exc, counter) => AzureTableUtils.AnalyzeReadException(exc.GetBaseException(), counter, TableName, Logger),
                        this.StoragePolicyOptions.OperationTimeout,
                        backoff);
#else
                    List<(T, string)> results = await executeQueryHandleContinuations();
#endif
                    // Data was read successfully if we got to here
                    return results;

                }
                catch (Exception exc)
                {
                    // Out of retries...
                    if (!AzureTableUtils.TableStorageDataNotFound(exc))
                    {
                        Logger.LogWarning((int)Utilities.ErrorCode.AzureTable_09, exc, "Failed to read Azure Storage table {TableName}", TableName);
                    }

                    throw new OrleansException($"Failed to read Azure Storage table {TableName}", exc);
                }
            }
            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }

        /// <summary>
        /// Inserts a set of new data entries into the table.
        /// Fails if the data does already exists.
        /// </summary>
        /// <param name="collection">Data entries to be inserted into the table.</param>
        /// <returns>Completion promise for this storage operation.</returns>
        public async Task BulkInsertTableEntries(IReadOnlyCollection<T> collection)
        {
            const string operation = "BulkInsertTableEntries";
            if (collection == null) throw new ArgumentNullException("collection");
            if (collection.Count > this.StoragePolicyOptions.MaxBulkUpdateRows)
            {
                throw new ArgumentOutOfRangeException("collection", collection.Count,
                        "Too many rows for bulk update - max " + this.StoragePolicyOptions.MaxBulkUpdateRows);
            }

            if (collection.Count == 0)
            {
                return;
            }

            var startTime = DateTime.UtcNow;
            if (Logger.IsEnabled(LogLevel.Trace)) Logger.LogTrace("{Operation} {Count} entries table {TableName}", operation, collection.Count, TableName);

            try
            {

                var entityBatch = new List<TableTransactionAction>(collection.Count);
                foreach (T entry in collection)
                {
                    entityBatch.Add(new TableTransactionAction(TableTransactionActionType.Add, entry));
                }

                try
                {
                    await Table.SubmitTransactionAsync(entityBatch);
                }
                catch (Exception exc)
                {
                    Logger.LogWarning((int)Utilities.ErrorCode.AzureTable_37, exc,
                        "Intermediate error bulk inserting {Count} entries in the table {TableName}", collection.Count, TableName);
                }
            }
            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }

        internal async Task<(string, string)> InsertTwoTableEntriesConditionallyAsync(T data1, T data2, string data2Etag)
        {
            const string operation = "InsertTableEntryConditionally";
            string data2Str = (data2 == null ? "null" : data2.ToString());
            var startTime = DateTime.UtcNow;

            if (Logger.IsEnabled(LogLevel.Trace)) Logger.LogTrace("{Operation} data1 {Data1} data2 {Data2} table {TableName}", operation, data1, data2Str, TableName);

            try
            {
                try
                {
                    data2.ETag = new ETag(data2Etag);
                    var opResults = await Table.SubmitTransactionAsync(new TableTransactionAction[]
                    {
                        new TableTransactionAction(TableTransactionActionType.Add, data1),
                        new TableTransactionAction(TableTransactionActionType.UpdateReplace, data2, data2.ETag)
                    });

                    //The batch results are returned in order of execution,
                    //see reference at https://msdn.microsoft.com/en-us/library/microsoft.windowsazure.storage.table.cloudtable.executebatch.aspx.
                    //The ETag of data is needed in further operations.
                    var resultETag1 = opResults.Value[0].Headers.ETag.GetValueOrDefault().ToString();
                    var resultETag2 = opResults.Value[1].Headers.ETag.GetValueOrDefault().ToString();
                    return (resultETag1, resultETag2);
                }
                catch (Exception exc)
                {
                    CheckAlertWriteError(operation, data1, data2Str, exc);
                    throw;
                }
            }
            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }

        internal async Task<(string, string)> UpdateTwoTableEntriesConditionallyAsync(T data1, string data1Etag, T data2, string data2Etag)
        {
            const string operation = "UpdateTableEntryConditionally";
            string data2Str = (data2 == null ? "null" : data2.ToString());
            var startTime = DateTime.UtcNow;
            if (Logger.IsEnabled(LogLevel.Trace)) Logger.LogTrace("{Operation} data1 {Data1} data2 {Data2} table {TableName}", operation, data1, data2Str, TableName);

            try
            {
                try
                {
                    data1.ETag = new ETag(data1Etag);
                    var entityBatch = new List<TableTransactionAction>(2);
                    entityBatch.Add(new TableTransactionAction(TableTransactionActionType.UpdateReplace, data1, data1.ETag));

                    if (data2 != null && data2Etag != null)
                    {
                        data2.ETag = new ETag(data2Etag);
                        entityBatch.Add(new TableTransactionAction(TableTransactionActionType.UpdateReplace, data2, data2.ETag));
                    }

                    var opResults = await Table.SubmitTransactionAsync(entityBatch);

                    //The batch results are returned in order of execution,
                    //see reference at https://msdn.microsoft.com/en-us/library/microsoft.windowsazure.storage.table.cloudtable.executebatch.aspx.
                    //The ETag of data is needed in further operations.
                    var resultETag1 = opResults.Value[0].Headers.ETag.GetValueOrDefault().ToString();
                    var resultETag2 = opResults.Value[1].Headers.ETag.GetValueOrDefault().ToString();
                    return (resultETag1, resultETag2);
                }
                catch (Exception exc)
                {
                    CheckAlertWriteError(operation, data1, data2Str, exc);
                    throw;
                }
            }
            finally
            {
                CheckAlertSlowAccess(startTime, operation);
            }
        }

        private async ValueTask<TableServiceClient> GetCloudTableCreationClientAsync()
        {
            try
            {
                return await options.CreateClient();
            }
            catch (Exception exc)
            {
                Logger.LogError((int)Utilities.ErrorCode.AzureTable_18, exc, "Error creating TableServiceClient.");
                throw;
            }
        }

        private void CheckAlertWriteError(string operation, object data1, string data2, Exception exc)
        {
            HttpStatusCode httpStatusCode;
            if (AzureTableUtils.EvaluateException(exc, out httpStatusCode, out _) && AzureTableUtils.IsContentionError(httpStatusCode))
            {
                // log at Verbose, since failure on conditional is not not an error. Will analyze and warn later, if required.
                if (Logger.IsEnabled(LogLevel.Debug)) Logger.LogDebug((int)Utilities.ErrorCode.AzureTable_13, exc,
                     "Intermediate Azure table write error {Operation} to table {TableName} data1 {Data1} data2 {Data2}", operation, TableName, (data1 ?? "null"), (data2 ?? "null"));

            }
            else
            {
                Logger.LogError((int)Utilities.ErrorCode.AzureTable_14, exc,
                    "Azure table access write error {Operation} to table {TableName} entry {Data1}", operation, TableName, data1);
            }
        }

        private void CheckAlertSlowAccess(DateTime startOperation, string operation)
        {
            var timeSpan = DateTime.UtcNow - startOperation;
            if (timeSpan > this.StoragePolicyOptions.OperationTimeout)
            {
                Logger.LogWarning((int)Utilities.ErrorCode.AzureTable_15, "Slow access to Azure Table {TableName} for {Operation}, which took {Duration}", TableName, operation, timeSpan);
            }
        }
    }

    internal static class TableDataManagerInternalExtensions
    {
        internal static IEnumerable<IEnumerable<TItem>> ToBatch<TItem>(this IEnumerable<TItem> source, int size)
        {
            using (IEnumerator<TItem> enumerator = source.GetEnumerator())
                while (enumerator.MoveNext())
                    yield return Take(enumerator, size);
        }

        private static IEnumerable<TItem> Take<TItem>(IEnumerator<TItem> source, int size)
        {
            int i = 0;
            do
                yield return source.Current;
            while (++i < size && source.MoveNext());
        }
    }
}


text:
namespace java.lang
{
    public partial class CloneNotSupportedException : Exception
    {
        public CloneNotSupportedException(){}
        public CloneNotSupportedException(String prm1){}
    }
}

redpajama_set_name: RedPajamaGithub


text:
package org.wildfly.extras.patch;

import java.io.Closeable;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.activation.DataHandler;
import javax.activation.DataSource;

import org.wildfly.extras.patch.Record.Action;
import org.wildfly.extras.patch.utils.IllegalArgumentAssertion;

/**
 * A smart patch defines add/update/delete records.
 *
 * A {@code SmartPatch} is immutable.
 *
 * @author thomas.diesler@jboss.com
 * @since 10-Jun-2015
 */
public final class SmartPatch implements Closeable {

    private final Patch patch;
    private final DataHandler dataHandler;
    private final Map<Path, Record> delMap = new HashMap<>();
    private final Map<Path, Record> updMap = new HashMap<>();
    private final Map<Path, Record> addMap = new HashMap<>();

    public static SmartPatch forInstall(Patch patch, DataHandler dataHandler) {
        IllegalArgumentAssertion.assertNotNull(dataHandler, "dataHandler");
        return new SmartPatch(patch, dataHandler);
    }

    public static SmartPatch forUninstall(Patch patch) {
        IllegalArgumentAssertion.assertNotNull(patch, "patch");
        PatchId patchId = patch.getPatchId();
        List<Record> records = new ArrayList<>();
        for (Record rec : patch.getRecords()) {
            records.add(Record.create(patchId, Action.DEL, rec.getPath(), rec.getChecksum()));
        }
        return new SmartPatch(Patch.create(patch.getMetadata(), records), null);
    }

    private SmartPatch(Patch patch, DataHandler dataHandler) {
        IllegalArgumentAssertion.assertNotNull(patch, "patch");
        this.patch = patch;
        this.dataHandler = dataHandler;
        for (Record rec : patch.getRecords()) {
            Record.Action action = rec.getAction();
            switch (rec.getAction()) {
                case ADD:
                    addMap.put(rec.getPath(), rec);
                    break;
                case UPD:
                    updMap.put(rec.getPath(), rec);
                    break;
                case DEL:
                    delMap.put(rec.getPath(), rec);
                    break;
                default:
                    throw new IllegalStateException(action + " no supported");
            }
        }
    }

    public PatchId getPatchId() {
        return patch.getPatchId();
    }

    public Patch getPatch() {
        return patch;
    }

    public DataHandler getDataHandler() {
        return dataHandler;
    }

    public boolean isUninstall() {
        return dataHandler == null;
    }

    public List<Record> getRecords() {
        return patch.getRecords();
    }

    public PatchMetadata getMetadata() {
        return patch.getMetadata();
    }

    public Set<Record> getRemoveSet() {
        return Collections.unmodifiableSet(new HashSet<>(delMap.values()));
    }

    public boolean isRemovePath(Path path) {
        return delMap.containsKey(path);
    }

	public Set<Record> getReplaceSet() {
        return Collections.unmodifiableSet(new HashSet<>(updMap.values()));
	}

    public boolean isReplacePath(Path path) {
        return updMap.containsKey(path);
    }

	public Set<Record> getAddSet() {
        return Collections.unmodifiableSet(new HashSet<>(addMap.values()));
	}

    public boolean isAddPath(Path path) {
        return addMap.containsKey(path);
    }

    @Override
    public void close() throws IOException {
        DataSource dataSource = dataHandler != null ? dataHandler.getDataSource() : null;
        if (dataSource instanceof Closeable) {
            ((Closeable) dataSource).close();
        }
    }

    @Override
    public String toString() {
        return "SmartPatch[id=" + patch.getPatchId() + ",add=" + addMap.size() + ",upd=" + updMap.size() + ",del=" + delMap.size() + "]";
    }
}


text:
class CreateArtists < ActiveRecord::Migration
  def change
    create_table :artists do |t|
      t.string :name
      t.string :url
      t.integer :followers

      t.timestamps null: false
    end
  end
end


text:
layout: default
title: Amber documentation
hide: true
next: getting-started.html
---

### Welcome

This is the Amber documentation website. The purpose of this documentation is to guide you step by step from learning how to install Amber to being an advanced Amber developer.

Currently we covered the firsts steps of installing and getting started with a new Amber project.

This documentation and guides are a community effort. You figured out how to do something useful or cool? Great! Share it with us! Contribute with your own guide here!

### What is Amber?

Amber is a language and development environment made for the web. The
language is deeply inspired by
[Smalltalk](http://en.wikipedia.org/wiki/Smalltalk), and compiles to
JavaScript files.

Amber includes a live development environment with a class browser,
workspace, unit test runner, transcript, object inspector and
debugger.

![IDE](/images/helios.png "Amber's IDE")

#### Why a Smalltalk dialect?

- Smalltalk stands head and shoulders above most other languages for
clarity, conciseness, and human-friendliness.

- As a language, it is immensely clean and mature, both syntactically
and semantically. It is a pure Object Oriented language, with objects all the way
down.

- Also, having a true live & incremental development environment where
you can build your application interactively in the browser is
unbeatable.

#### What about the JavaScript ecosystem?

Amber plays very well with the outer world. You can interact with
JavaScript objects seamlessly, and even inspect them as any Amber
object.

Evaluating JavaScript object methods is transparent and makes using
libraries a breeze.

Installing Amber.


text:
#import "NSObject.h"

// Not exported
@interface ML3QueryResultSet_BackingStore : NSObject
{
    vector_c1c297d2 _persistentIDs;
    struct vector<unsigned char, std::__1::allocator<unsigned char>> _sections;
}

- (id).cxx_construct;
- (void).cxx_destruct;
- (id)backingStoreByRemovingPersistentIDs:(const unordered_set_f2692727 *)arg1;
- (void)reverseEnumerateSectionsUsingBlock:(id)arg1;
- (void)enumerateSectionsUsingBlock:(id)arg1;
- (_Bool)containsPersistentIDs:(const unordered_set_f2692727 *)arg1;
- (void)reverseEnumeratePersistentIDsUsingBlock:(id)arg1;
- (void)enumeratePersistentIDsUsingBlock:(id)arg1;
- (long long)persistentIDAtIndex:(unsigned long long)arg1;
@property(readonly, nonatomic) unsigned long long count;

@end


text:
package org.apache.commons.collections.bag;

import java.util.Comparator;

import org.apache.commons.collections.Bag;
import org.apache.commons.collections.SortedBag;

/**
 * Decorates another SortedBag to synchronize its behaviour
 * for a multi-threaded environment.
 *

 * Methods are synchronized, then forwarded to the decorated bag.
 * Iterators must be separately synchronized around the loop.
 *

 * This class is Serializable from Commons Collections 3.1.
 *
 * @since Commons Collections 3.0
 * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $
 *
 * @author Stephen Colebourne
 */
public class SynchronizedSortedBag
        extends SynchronizedBag implements SortedBag {

    /** Serialization version */
    private static final long serialVersionUID = 722374056718497858L;

    /**
     * Factory method to create a synchronized sorted bag.
     *
     * @param bag  the bag to decorate, must not be null
     * @return a new synchronized SortedBag
     * @throws IllegalArgumentException if bag is null
     */
    public static SortedBag decorate(SortedBag bag) {
        return new SynchronizedSortedBag(bag);
    }

    //-----------------------------------------------------------------------
    /**
     * Constructor that wraps (not copies).
     *
     * @param bag  the bag to decorate, must not be null
     * @throws IllegalArgumentException if bag is null
     */
    protected SynchronizedSortedBag(SortedBag bag) {
        super(bag);
    }

    /**
     * Constructor that wraps (not copies).
     *
     * @param bag  the bag to decorate, must not be null
     * @param lock  the lock to use, must not be null
     * @throws IllegalArgumentException if bag is null
     */
    protected SynchronizedSortedBag(Bag bag, Object lock) {
        super(bag, lock);
    }

    /**
     * Gets the bag being decorated.
     *
     * @return the decorated bag
     */
    protected SortedBag getSortedBag() {
        return (SortedBag) collection;
    }

    //-----------------------------------------------------------------------
    public synchronized Object first() {
        synchronized (lock) {
            return getSortedBag().first();
        }
    }

    public synchronized Object last() {
        synchronized (lock) {
            return getSortedBag().last();
        }
    }

    public synchronized Comparator comparator() {
        synchronized (lock) {
            return getSortedBag().comparator();
        }
    }

}


text:
require 'helper'

class TestSchemaURI < Minitest::Test
  def test_default_uri
    assert_equal URI('#'), schema_uri
  end

  def test_schema_uri_when_parent_is_not_specified
    assert_equal URI('test'), schema_uri('test')
  end

  def test_schema_uri_when_parent_uri_is_absolute
    uri = schema_uri('test', 'http://example.com/')
    assert_equal URI('http://example.com/test'), uri
  end

  def test_schema_uri_when_parent_uri_is_relative
    assert_raises(JSchema::InvalidSchema) do
      schema_uri('relative/', 'relative/')
    end
  end

  def test_schema_uri_when_both_parent_and_schema_uri_are_absolute
    schema_id = 'http://example.com/'
    parent_id = 'http://localhost/'
    uri = schema_uri(schema_id, parent_id)
    assert_equal URI(schema_id), uri
  end

  def test_schema_uri_when_ids_are_not_specified
    assert_equal URI('#/child'), schema_uri(nil, nil, 'child')
  end

  def test_that_schema_uri_is_normalized
    uri = schema_uri('etc/../path', 'http://Example.com')
    assert_equal URI('http://example.com/path'), uri
  end

  def test_that_implicit_schema_id_treated_as_uri_fragment
    uri = schema_uri(nil, 'http://example.com/path#sch', 'sub')
    assert_equal URI('http://example.com/path#sch/sub'), uri
  end

  def test_escaping
    uri = schema_uri(nil, '#/definitions', 'schema%')
    assert_equal URI('#/definitions/schema%25'), uri
  end

  private

  def schema_uri(schema_id = nil, parent_id = nil, id = nil)
    parent_schema = JSchema::Schema.build('id' => parent_id) if parent_id || id
    JSchema::SchemaURI.build(schema_id, parent_schema, id)
  end
end


text:
/**
 * JNI implementation of the OpenCL function callbacks.
 *
 * @author Spasi
 */

#include <jni.h>
#include "common_tools.h"
#include "extcl.h"
#include "org_lwjgl_opencl_CallbackUtil.h"

static jmethodID contextCallbackJ;
static jmethodID memObjectDestructorCallbackJ;
static jmethodID programCallbackJ;
static jmethodID nativeKernelCallbackJ;
static jmethodID eventCallbackJ;
static jmethodID printfCallbackJ;

JNIEXPORT jlong JNICALL Java_org_lwjgl_opencl_CallbackUtil_ncreateGlobalRef(JNIEnv *env, jclass clazz, jobject obj) {
	return (intptr_t)(*env)->NewGlobalRef(env, obj);
}

JNIEXPORT void JNICALL Java_org_lwjgl_opencl_CallbackUtil_deleteGlobalRef(JNIEnv *env, jclass clazz, jlong globalRef) {
    (*env)->DeleteGlobalRef(env, (jobject)(intptr_t)globalRef);
}

// ----------------- [ CONTEXT CALLBACK ] -----------------

static void CL_CALLBACK contextCallback(const char *errinfo, const void *private_info, size_t cb, void *user_data) {
    JNIEnv *env = attachCurrentThread();
    jobject private_info_buffer = NULL;

	if ( env != NULL && !(*env)->ExceptionOccurred(env) && contextCallbackJ != NULL ) {
        if ( private_info != NULL )
            private_info_buffer = NewReadOnlyDirectByteBuffer(env, private_info, cb);

        (*env)->CallVoidMethod(env, (jobject)user_data, contextCallbackJ,
            NewStringNativeWithLength(env, errinfo, (jsize)strlen(errinfo)),
            private_info_buffer
        );
    }

    detachCurrentThread();
}

JNIEXPORT jlong JNICALL Java_org_lwjgl_opencl_CallbackUtil_getContextCallback(JNIEnv *env, jclass clazz) {
    // Cache the callback methodID
    jclass callbackClass;
    if ( contextCallbackJ == NULL ) {
        callbackClass = (*env)->FindClass(env, "org/lwjgl/opencl/CLContextCallback");
        if ( callbackClass != NULL )
            contextCallbackJ = (*env)->GetMethodID(env, callbackClass, "handleMessage", "(Ljava/lang/String;Ljava/nio/ByteBuffer;)V");
    }

    return (jlong)(intptr_t)&contextCallback;
}

// ----------------- [ MEM OBJECT DESTRUCTOR CALLBACK ] -----------------

static void CL_CALLBACK memObjectDestructorCallback(cl_mem memobj, void *user_data) {
    JNIEnv *env = attachCurrentThread();

	if ( env != NULL && !(*env)->ExceptionOccurred(env) && memObjectDestructorCallbackJ != NULL ) {
        (*env)->CallVoidMethod(env, (jobject)user_data, memObjectDestructorCallbackJ,
            (jlong)(intptr_t)memobj
        );
        (*env)->DeleteGlobalRef(env, (jobject)user_data);
    }

    detachCurrentThread();
}

JNIEXPORT jlong JNICALL Java_org_lwjgl_opencl_CallbackUtil_getMemObjectDestructorCallback(JNIEnv *env, jclass clazz) {
    // Cache the callback methodID
    jclass callbackClass;
    if ( memObjectDestructorCallbackJ == NULL ) {
        callbackClass = (*env)->FindClass(env, "org/lwjgl/opencl/CLMemObjectDestructorCallback");
        if ( callbackClass != NULL )
            memObjectDestructorCallbackJ = (*env)->GetMethodID(env, callbackClass, "handleMessage", "(J)V");
    }

    return (jlong)(intptr_t)&memObjectDestructorCallback;
}

// ----------------- [ PROGRAM CALLBACK ] -----------------

static void CL_CALLBACK programCallback(cl_program program, void *user_data) {
    JNIEnv *env = attachCurrentThread();

	if ( env != NULL && !(*env)->ExceptionOccurred(env) && programCallbackJ != NULL ) {
        (*env)->CallVoidMethod(env, (jobject)user_data, programCallbackJ,
            (jlong)(intptr_t)program
        );
        (*env)->DeleteGlobalRef(env, (jobject)user_data);
    }

    detachCurrentThread();
}

JNIEXPORT jlong JNICALL Java_org_lwjgl_opencl_CallbackUtil_getProgramCallback(JNIEnv *env, jclass clazz) {
    // Cache the callback methodID
    jclass callbackClass;
    if ( programCallbackJ == NULL ) {
        callbackClass = (*env)->FindClass(env, "org/lwjgl/opencl/CLProgramCallback");
        if ( callbackClass != NULL )
            programCallbackJ = (*env)->GetMethodID(env, callbackClass, "handleMessage", "(J)V");
    }

	return (jlong)(intptr_t)&programCallback;
}

// ----------------- [ NATIVE KERNEL CALLBACK ] -----------------

static void CL_CALLBACK nativeKernelCallback(void *args) {
    JNIEnv *env = attachCurrentThread();
    jobject user_func = (jobject)(intptr_t)*(jlong *)args;
    jsize num_mem_objects = *(jsize *)((char *)args + 8);
    jobjectArray memobjs = NULL;
    jobject buffer;
    jsize i;

	if ( env != NULL && !(*env)->ExceptionOccurred(env) && nativeKernelCallbackJ != NULL ) {
        if ( num_mem_objects > 0 ) {
            memobjs = (*env)->NewObjectArray(env, num_mem_objects, (*env)->FindClass(env, "java/nio/ByteBuffer"), NULL);
            for ( i = 0; i < num_mem_objects; i++ ) {
                buffer = (*env)->NewDirectByteBuffer(env,
                    // Pointer to cl_mem buffer
                    (void *)((char *)args + (12 + 4 + (i * (4 + sizeof(void *))))),
                    // cl_mem buffer size
                    *((jint *)((char *)args + (12 + (i * (4 + sizeof(void *))))))
                );
                (*env)->SetObjectArrayElement(env, memobjs, i, buffer);
            }
        }

        (*env)->CallVoidMethod(env, user_func, nativeKernelCallbackJ, memobjs);

        if ( num_mem_objects > 0 )
            (*env)->DeleteLocalRef(env, memobjs);
        (*env)->DeleteGlobalRef(env, user_func);
    }

    detachCurrentThread();
}

JNIEXPORT jlong JNICALL Java_org_lwjgl_opencl_CallbackUtil_getNativeKernelCallback(JNIEnv *env, jclass clazz) {
    // Cache the callback methodID
    jclass callbackClass;
    if ( nativeKernelCallbackJ == NULL ) {
        callbackClass = (*env)->FindClass(env, "org/lwjgl/opencl/CLNativeKernel");
        if ( callbackClass != NULL )
            nativeKernelCallbackJ = (*env)->GetMethodID(env, callbackClass, "execute", "([Ljava/nio/ByteBuffer;)V");
    }

    return (jlong)(intptr_t)&nativeKernelCallback;
}

// ----------------- [ EVENT CALLBACK ] -----------------

static void CL_CALLBACK eventCallback(cl_event event, cl_int event_command_exec_status, void *user_data) {
    JNIEnv *env = attachCurrentThread();

	if ( env != NULL && !(*env)->ExceptionOccurred(env) && eventCallbackJ != NULL ) {
        (*env)->CallVoidMethod(env, (jobject)user_data, eventCallbackJ,
            (jlong)(intptr_t)event,
            event_command_exec_status
        );
        (*env)->DeleteGlobalRef(env, (jobject)user_data);
    }

    detachCurrentThread();
}

JNIEXPORT jlong JNICALL Java_org_lwjgl_opencl_CallbackUtil_getEventCallback(JNIEnv *env, jclass clazz) {
    // Cache the callback methodID
    jclass callbackClass;
    if ( eventCallbackJ == NULL ) {
        callbackClass = (*env)->FindClass(env, "org/lwjgl/opencl/CLEventCallback");
        if ( callbackClass != NULL )
            eventCallbackJ = (*env)->GetMethodID(env, callbackClass, "handleMessage", "(JI)V");
    }

    return (jlong)(intptr_t)&eventCallback;
}

// ----------------- [ PRINTF CALLBACK ] -----------------

static void CL_CALLBACK printfCallback(cl_context context, cl_uint printf_data_len, char *printf_data_ptr, void *user_data) {
    JNIEnv *env = attachCurrentThread();

	if ( env != NULL && !(*env)->ExceptionOccurred(env) && printfCallbackJ != NULL ) {
        (*env)->CallVoidMethod(env, (jobject)user_data, printfCallbackJ,
            NewStringNativeWithLength(env, printf_data_ptr, printf_data_len)
        );
    }

    detachCurrentThread();
}

JNIEXPORT jlong JNICALL Java_org_lwjgl_opencl_CallbackUtil_getPrintfCallback(JNIEnv *env, jclass clazz) {
    // Cache the callback methodID
    jclass callbackClass;
    if ( printfCallbackJ == NULL ) {
        callbackClass = (*env)->FindClass(env, "org/lwjgl/opencl/CLPrintfCallback");
        if ( callbackClass != NULL )
            printfCallbackJ = (*env)->GetMethodID(env, callbackClass, "handleMessage", "(Ljava/lang/String;)V");
    }

    return (jlong)(intptr_t)&printfCallback;
}

// ----------------- [ APPLE_ContextLoggingFunctions CALLBACKS ] -----------------

JNIEXPORT jlong JNICALL Java_org_lwjgl_opencl_CallbackUtil_getLogMessageToSystemLogAPPLE(JNIEnv *env, jclass clazz) {
    return (jlong)(intptr_t)extcl_GetProcAddress("clLogMessagesToSystemLogAPPLE");
}

JNIEXPORT jlong JNICALL Java_org_lwjgl_opencl_CallbackUtil_getLogMessageToStdoutAPPLE(JNIEnv *env, jclass clazz) {
    return (jlong)(intptr_t)extcl_GetProcAddress("getLogMessageToStdoutAPPLE");
}

JNIEXPORT jlong JNICALL Java_org_lwjgl_opencl_CallbackUtil_getLogMessageToStderrAPPLE(JNIEnv *env, jclass clazz) {
    return (jlong)(intptr_t)extcl_GetProcAddress("getLogMessageToStderrAPPLE");
}


text:
'use strict';

module.exports = require('./is-implemented')() ? Math.tanh : require('./shim');

//# sourceMappingURL=index-compiled.js.map


text:
<?php

declare(strict_types=1);

namespace Jose\Component\Core;

interface Algorithm
{
    /**
     * Returns the name of the algorithm.
     */
    public function name(): string;

    /**
     * Returns the key types suitable for this algorithm (e.g. "oct", "RSA"...).
     *
     * @return string[]
     */
    public function allowedKeyTypes(): array;
}


text:
<?php

include "config.inc.php";
include "utility.inc.php";
include "token.inc.php";
include "database.inc.php";

// Check, whether we are logged in
$jwt = Token::current_token();
if (!$jwt || !Token::is_authorized($jwt)) {
    // We are not (properly) logged in, redirect to index.php
    echo utility_get_redirect_page_header("index.php");
    echo utility_get_default_page_footer();
    die();
}

// Get basic user info
$username = Token::get_username($jwt);
$admin_status = Token::get_admin($jwt);

// Get the reservation id and its type
$res_id = $_POST['reservation_id'];
$type = $_POST['type'];

echo utility_get_default_page_header("Varauksen poisto");

// Get the reservation from database
$db = new Database;
$db->open();
$reservation = $db->get_reservation_details_by_type($res_id, $type);
if (!$reservation) {
    // Reservation not found.
    echo utility_get_fail_message("Varausta ei löydy.");
    echo utility_get_default_page_footer();
} else if ($admin_status || $reservation->reservee->username == $username) {
    // Reservation was found and we have authority to remove it.
    if ($db->remove_reservation($reservation)) {
        echo utility_get_success_message("Varaus poistettu!");
    } else {
        echo utility_get_fail_message("Varausta ei voida poistaa!");
    }
} else {
    // No authority to remove the reservation.
    echo utility_get_fail_message("Voit poistaa vain omia varauksiasi!");
}

$db->close();

echo utility_get_default_page_footer();

?>


text:
import { Page } from '@playwright/test';

class QuillPage {
  constructor(private page: Page) {}

  get root() {
    return this.page.locator('.ql-editor');
  }

  editorHTML() {
    return this.root.innerHTML();
  }
}

export default QuillPage;


text:
//  Copyright (c) 2013, Facebook, Inc.  All rights reserved.
//  This source code is licensed under the BSD-style license found in the
//  LICENSE file in the root directory of this source tree. An additional grant
//  of patent rights can be found in the PATENTS file in the same directory.
//
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.

#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif

#include <inttypes.h>
#include <algorithm>
#include <iostream>
#include <mutex>
#include <queue>
#include <set>
#include <thread>
#include <unordered_set>
#include <utility>

#include "db/db_impl.h"
#include "db/dbformat.h"
#include "db/filename.h"
#include "db/job_context.h"
#include "db/version_set.h"
#include "db/write_batch_internal.h"
#include "port/stack_trace.h"
#include "rocksdb/cache.h"
#include "rocksdb/compaction_filter.h"
#include "rocksdb/convenience.h"
#include "rocksdb/db.h"
#include "rocksdb/env.h"
#include "rocksdb/experimental.h"
#include "rocksdb/filter_policy.h"
#include "rocksdb/options.h"
#include "rocksdb/perf_context.h"
#include "rocksdb/slice.h"
#include "rocksdb/slice_transform.h"
#include "rocksdb/table.h"
#include "rocksdb/table_properties.h"
#include "rocksdb/thread_status.h"
#include "rocksdb/utilities/checkpoint.h"
#include "rocksdb/utilities/write_batch_with_index.h"
#include "table/block_based_table_factory.h"
#include "table/mock_table.h"
#include "table/plain_table_factory.h"
#include "util/compression.h"
#include "util/hash.h"
#include "util/hash_linklist_rep.h"
#include "util/logging.h"
#include "util/mock_env.h"
#include "util/mutexlock.h"
#include "util/rate_limiter.h"
#include "util/scoped_arena_iterator.h"
#include "util/statistics.h"
#include "util/string_util.h"
#include "util/sync_point.h"
#include "util/testharness.h"
#include "util/testutil.h"
#include "util/thread_status_util.h"
#include "util/xfunc.h"
#include "utilities/merge_operators.h"

#if !defined(IOS_CROSS_COMPILE) && (!defined(NDEBUG) || !defined(OS_WIN))
#ifndef ROCKSDB_LITE
namespace rocksdb {

static std::string RandomString(Random* rnd, int len, double ratio) {
  std::string r;
  test::CompressibleString(rnd, ratio, len, &r);
  return r;
}

std::string Key(uint64_t key, int length) {
  const int kBufSize = 1000;
  char buf[kBufSize];
  if (length > kBufSize) {
    length = kBufSize;
  }
  snprintf(buf, kBufSize, "%0*" PRIu64, length, key);
  return std::string(buf);
}

class CompactionJobStatsTest : public testing::Test {
 public:
  std::string dbname_;
  std::string alternative_wal_dir_;
  Env* env_;
  DB* db_;
  std::vector<ColumnFamilyHandle*> handles_;

  Options last_options_;

  CompactionJobStatsTest() : env_(Env::Default()) {
    env_->SetBackgroundThreads(1, Env::LOW);
    env_->SetBackgroundThreads(1, Env::HIGH);
    dbname_ = test::TmpDir(env_) + "/compaction_job_stats_test";
    alternative_wal_dir_ = dbname_ + "/wal";
    Options options;
    options.create_if_missing = true;
    auto delete_options = options;
    delete_options.wal_dir = alternative_wal_dir_;
    EXPECT_OK(DestroyDB(dbname_, delete_options));
    // Destroy it for not alternative WAL dir is used.
    EXPECT_OK(DestroyDB(dbname_, options));
    db_ = nullptr;
    Reopen(options);
  }

  ~CompactionJobStatsTest() {
    rocksdb::SyncPoint::GetInstance()->DisableProcessing();
    rocksdb::SyncPoint::GetInstance()->LoadDependency({});
    rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
    Close();
    Options options;
    options.db_paths.emplace_back(dbname_, 0);
    options.db_paths.emplace_back(dbname_ + "_2", 0);
    options.db_paths.emplace_back(dbname_ + "_3", 0);
    options.db_paths.emplace_back(dbname_ + "_4", 0);
    EXPECT_OK(DestroyDB(dbname_, options));
  }

  DBImpl* dbfull() {
    return reinterpret_cast<DBImpl*>(db_);
  }

  void CreateColumnFamilies(const std::vector<std::string>& cfs,
                            const Options& options) {
    ColumnFamilyOptions cf_opts(options);
    size_t cfi = handles_.size();
    handles_.resize(cfi + cfs.size());
    for (auto cf : cfs) {
      ASSERT_OK(db_->CreateColumnFamily(cf_opts, cf, &handles_[cfi++]));
    }
  }

  void CreateAndReopenWithCF(const std::vector<std::string>& cfs,
                             const Options& options) {
    CreateColumnFamilies(cfs, options);
    std::vector<std::string> cfs_plus_default = cfs;
    cfs_plus_default.insert(cfs_plus_default.begin(), kDefaultColumnFamilyName);
    ReopenWithColumnFamilies(cfs_plus_default, options);
  }

  void ReopenWithColumnFamilies(const std::vector<std::string>& cfs,
                                const std::vector<Options>& options) {
    ASSERT_OK(TryReopenWithColumnFamilies(cfs, options));
  }

  void ReopenWithColumnFamilies(const std::vector<std::string>& cfs,
                                const Options& options) {
    ASSERT_OK(TryReopenWithColumnFamilies(cfs, options));
  }

  Status TryReopenWithColumnFamilies(
      const std::vector<std::string>& cfs,
      const std::vector<Options>& options) {
    Close();
    EXPECT_EQ(cfs.size(), options.size());
    std::vector<ColumnFamilyDescriptor> column_families;
    for (size_t i = 0; i < cfs.size(); ++i) {
      column_families.push_back(ColumnFamilyDescriptor(cfs[i], options[i]));
    }
    DBOptions db_opts = DBOptions(options[0]);
    return DB::Open(db_opts, dbname_, column_families, &handles_, &db_);
  }

  Status TryReopenWithColumnFamilies(const std::vector<std::string>& cfs,
                                     const Options& options) {
    Close();
    std::vector<Options> v_opts(cfs.size(), options);
    return TryReopenWithColumnFamilies(cfs, v_opts);
  }

  void Reopen(const Options& options) {
    ASSERT_OK(TryReopen(options));
  }

  void Close() {
    for (auto h : handles_) {
      delete h;
    }
    handles_.clear();
    delete db_;
    db_ = nullptr;
  }

  void DestroyAndReopen(const Options& options) {
    // Destroy using last options
    Destroy(last_options_);
    ASSERT_OK(TryReopen(options));
  }

  void Destroy(const Options& options) {
    Close();
    ASSERT_OK(DestroyDB(dbname_, options));
  }

  Status ReadOnlyReopen(const Options& options) {
    return DB::OpenForReadOnly(options, dbname_, &db_);
  }

  Status TryReopen(const Options& options) {
    Close();
    last_options_ = options;
    return DB::Open(options, dbname_, &db_);
  }

  Status Flush(int cf = 0) {
    if (cf == 0) {
      return db_->Flush(FlushOptions());
    } else {
      return db_->Flush(FlushOptions(), handles_[cf]);
    }
  }

  Status Put(const Slice& k, const Slice& v, WriteOptions wo = WriteOptions()) {
    return db_->Put(wo, k, v);
  }

  Status Put(int cf, const Slice& k, const Slice& v,
             WriteOptions wo = WriteOptions()) {
    return db_->Put(wo, handles_[cf], k, v);
  }

  Status Delete(const std::string& k) {
    return db_->Delete(WriteOptions(), k);
  }

  Status Delete(int cf, const std::string& k) {
    return db_->Delete(WriteOptions(), handles_[cf], k);
  }

  std::string Get(const std::string& k, const Snapshot* snapshot = nullptr) {
    ReadOptions options;
    options.verify_checksums = true;
    options.snapshot = snapshot;
    std::string result;
    Status s = db_->Get(options, k, &result);
    if (s.IsNotFound()) {
      result = "NOT_FOUND";
    } else if (!s.ok()) {
      result = s.ToString();
    }
    return result;
  }

  std::string Get(int cf, const std::string& k,
                  const Snapshot* snapshot = nullptr) {
    ReadOptions options;
    options.verify_checksums = true;
    options.snapshot = snapshot;
    std::string result;
    Status s = db_->Get(options, handles_[cf], k, &result);
    if (s.IsNotFound()) {
      result = "NOT_FOUND";
    } else if (!s.ok()) {
      result = s.ToString();
    }
    return result;
  }

  int NumTableFilesAtLevel(int level, int cf = 0) {
    std::string property;
    if (cf == 0) {
      // default cfd
      EXPECT_TRUE(db_->GetProperty(
          "rocksdb.num-files-at-level" + NumberToString(level), &property));
    } else {
      EXPECT_TRUE(db_->GetProperty(
          handles_[cf], "rocksdb.num-files-at-level" + NumberToString(level),
          &property));
    }
    return atoi(property.c_str());
  }

  // Return spread of files per level
  std::string FilesPerLevel(int cf = 0) {
    int num_levels =
        (cf == 0) ? db_->NumberLevels() : db_->NumberLevels(handles_[1]);
    std::string result;
    size_t last_non_zero_offset = 0;
    for (int level = 0; level < num_levels; level++) {
      int f = NumTableFilesAtLevel(level, cf);
      char buf[100];
      snprintf(buf, sizeof(buf), "%s%d", (level ? "," : ""), f);
      result += buf;
      if (f > 0) {
        last_non_zero_offset = result.size();
      }
    }
    result.resize(last_non_zero_offset);
    return result;
  }

  uint64_t Size(const Slice& start, const Slice& limit, int cf = 0) {
    Range r(start, limit);
    uint64_t size;
    if (cf == 0) {
      db_->GetApproximateSizes(&r, 1, &size);
    } else {
      db_->GetApproximateSizes(handles_[1], &r, 1, &size);
    }
    return size;
  }

  void Compact(int cf, const Slice& start, const Slice& limit,
               uint32_t target_path_id) {
    CompactRangeOptions compact_options;
    compact_options.target_path_id = target_path_id;
    ASSERT_OK(db_->CompactRange(compact_options, handles_[cf], &start, &limit));
  }

  void Compact(int cf, const Slice& start, const Slice& limit) {
    ASSERT_OK(
        db_->CompactRange(CompactRangeOptions(), handles_[cf], &start, &limit));
  }

  void Compact(const Slice& start, const Slice& limit) {
    ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &start, &limit));
  }

  void TEST_Compact(int level, int cf, const Slice& start, const Slice& limit) {
    ASSERT_OK(dbfull()->TEST_CompactRange(level, &start, &limit, handles_[cf],
                                          true /* disallow trivial move */));
  }

  // Do n memtable compactions, each of which produces an sstable
  // covering the range [small,large].
  void MakeTables(int n, const std::string& small, const std::string& large,
                  int cf = 0) {
    for (int i = 0; i < n; i++) {
      ASSERT_OK(Put(cf, small, "begin"));
      ASSERT_OK(Put(cf, large, "end"));
      ASSERT_OK(Flush(cf));
    }
  }

  static void SetDeletionCompactionStats(
      CompactionJobStats *stats, uint64_t input_deletions,
      uint64_t expired_deletions, uint64_t records_replaced) {
    stats->num_input_deletion_records = input_deletions;
    stats->num_expired_deletion_records = expired_deletions;
    stats->num_records_replaced = records_replaced;
  }

  void MakeTableWithKeyValues(
    Random* rnd, uint64_t smallest, uint64_t largest,
    int key_size, int value_size, uint64_t interval,
    double ratio, int cf = 0) {
    for (auto key = smallest; key < largest; key += interval) {
      ASSERT_OK(Put(cf, Slice(Key(key, key_size)),
                        Slice(RandomString(rnd, value_size, ratio))));
    }
    ASSERT_OK(Flush(cf));
  }

  // This function behaves with the implicit understanding that two
  // rounds of keys are inserted into the database, as per the behavior
  // of the DeletionStatsTest.
  void SelectivelyDeleteKeys(uint64_t smallest, uint64_t largest,
    uint64_t interval, int deletion_interval, int key_size,
    uint64_t cutoff_key_num, CompactionJobStats* stats, int cf = 0) {

    // interval needs to be >= 2 so that deletion entries can be inserted
    // that are intended to not result in an actual key deletion by using
    // an offset of 1 from another existing key
    ASSERT_GE(interval, 2);

    uint64_t ctr = 1;
    uint32_t deletions_made = 0;
    uint32_t num_deleted = 0;
    uint32_t num_expired = 0;
    for (auto key = smallest; key <= largest; key += interval, ctr++) {
      if (ctr % deletion_interval == 0) {
        ASSERT_OK(Delete(cf, Key(key, key_size)));
        deletions_made++;
        num_deleted++;

        if (key > cutoff_key_num) {
          num_expired++;
        }
      }
    }

    // Insert some deletions for keys that don't exist that
    // are both in and out of the key range
    ASSERT_OK(Delete(cf, Key(smallest+1, key_size)));
    deletions_made++;

    ASSERT_OK(Delete(cf, Key(smallest-1, key_size)));
    deletions_made++;
    num_expired++;

    ASSERT_OK(Delete(cf, Key(smallest-9, key_size)));
    deletions_made++;
    num_expired++;

    ASSERT_OK(Flush(cf));
    SetDeletionCompactionStats(stats, deletions_made, num_expired,
      num_deleted);
  }
};

// An EventListener which helps verify the compaction results in
// test CompactionJobStatsTest.
class CompactionJobStatsChecker : public EventListener {
 public:
  CompactionJobStatsChecker() : compression_enabled_(false) {}

  size_t NumberOfUnverifiedStats() { return expected_stats_.size(); }

  // Once a compaction completed, this function will verify the returned
  // CompactionJobInfo with the oldest CompactionJobInfo added earlier
  // in "expected_stats_" which has not yet being used for verification.
  virtual void OnCompactionCompleted(DB *db, const CompactionJobInfo& ci) {
    std::lock_guard<std::mutex> lock(mutex_);
    if (expected_stats_.size()) {
      Verify(ci.stats, expected_stats_.front());
      expected_stats_.pop();
    }
  }

  // A helper function which verifies whether two CompactionJobStats
  // match.  The verification of all compaction stats are done by
  // ASSERT_EQ except for the total input / output bytes, which we
  // use ASSERT_GE and ASSERT_LE with a reasonable bias ---
  // 10% in uncompressed case and 20% when compression is used.
  virtual void Verify(const CompactionJobStats& current_stats,
              const CompactionJobStats& stats) {
    // time
    ASSERT_GT(current_stats.elapsed_micros, 0U);

    ASSERT_EQ(current_stats.num_input_records,
        stats.num_input_records);
    ASSERT_EQ(current_stats.num_input_files,
        stats.num_input_files);
    ASSERT_EQ(current_stats.num_input_files_at_output_level,
        stats.num_input_files_at_output_level);

    ASSERT_EQ(current_stats.num_output_records,
        stats.num_output_records);
    ASSERT_EQ(current_stats.num_output_files,
        stats.num_output_files);

    ASSERT_EQ(current_stats.is_manual_compaction,
        stats.is_manual_compaction);

    // file size
    double kFileSizeBias = compression_enabled_ ? 0.20 : 0.10;
    ASSERT_GE(current_stats.total_input_bytes * (1.00 + kFileSizeBias),
              stats.total_input_bytes);
    ASSERT_LE(current_stats.total_input_bytes,
              stats.total_input_bytes * (1.00 + kFileSizeBias));
    ASSERT_GE(current_stats.total_output_bytes * (1.00 + kFileSizeBias),
              stats.total_output_bytes);
    ASSERT_LE(current_stats.total_output_bytes,
              stats.total_output_bytes * (1.00 + kFileSizeBias));
    ASSERT_EQ(current_stats.total_input_raw_key_bytes,
              stats.total_input_raw_key_bytes);
    ASSERT_EQ(current_stats.total_input_raw_value_bytes,
              stats.total_input_raw_value_bytes);

    ASSERT_EQ(current_stats.num_records_replaced,
        stats.num_records_replaced);

    ASSERT_EQ(current_stats.num_corrupt_keys,
        stats.num_corrupt_keys);

    ASSERT_EQ(
        std::string(current_stats.smallest_output_key_prefix),
        std::string(stats.smallest_output_key_prefix));
    ASSERT_EQ(
        std::string(current_stats.largest_output_key_prefix),
        std::string(stats.largest_output_key_prefix));
  }

  // Add an expected compaction stats, which will be used to
  // verify the CompactionJobStats returned by the OnCompactionCompleted()
  // callback.
  void AddExpectedStats(const CompactionJobStats& stats) {
    std::lock_guard<std::mutex> lock(mutex_);
    expected_stats_.push(stats);
  }

  void EnableCompression(bool flag) {
    compression_enabled_ = flag;
  }

 private:
  std::mutex mutex_;
  std::queue<CompactionJobStats> expected_stats_;
  bool compression_enabled_;
};

// An EventListener which helps verify the compaction statistics in
// the test DeletionStatsTest.
class CompactionJobDeletionStatsChecker : public CompactionJobStatsChecker {
 public:
  // Verifies whether two CompactionJobStats match.
  void Verify(const CompactionJobStats& current_stats,
              const CompactionJobStats& stats) {
    ASSERT_EQ(
      current_stats.num_input_deletion_records,
      stats.num_input_deletion_records);
    ASSERT_EQ(
        current_stats.num_expired_deletion_records,
        stats.num_expired_deletion_records);
    ASSERT_EQ(
        current_stats.num_records_replaced,
        stats.num_records_replaced);

    ASSERT_EQ(current_stats.num_corrupt_keys,
        stats.num_corrupt_keys);
  }
};

namespace {

uint64_t EstimatedFileSize(
    uint64_t num_records, size_t key_size, size_t value_size,
    double compression_ratio = 1.0,
    size_t block_size = 4096,
    int bloom_bits_per_key = 10) {
  const size_t kPerKeyOverhead = 8;
  const size_t kFooterSize = 512;

  uint64_t data_size =
      num_records * (key_size + value_size * compression_ratio +
                     kPerKeyOverhead);

  return data_size + kFooterSize
         + num_records * bloom_bits_per_key / 8      // filter block
         + data_size * (key_size + 8) / block_size;  // index block
}

namespace {

void CopyPrefix(
    const Slice& src, size_t prefix_length, std::string* dst) {
  assert(prefix_length > 0);
  size_t length = src.size() > prefix_length ? prefix_length : src.size();
  dst->assign(src.data(), length);
}

}  // namespace

CompactionJobStats NewManualCompactionJobStats(
    const std::string& smallest_key, const std::string& largest_key,
    size_t num_input_files, size_t num_input_files_at_output_level,
    uint64_t num_input_records, size_t key_size, size_t value_size,
    size_t num_output_files, uint64_t num_output_records,
    double compression_ratio, uint64_t num_records_replaced,
    bool is_manual = true) {
  CompactionJobStats stats;
  stats.Reset();

  stats.num_input_records = num_input_records;
  stats.num_input_files = num_input_files;
  stats.num_input_files_at_output_level = num_input_files_at_output_level;

  stats.num_output_records = num_output_records;
  stats.num_output_files = num_output_files;

  stats.total_input_bytes =
      EstimatedFileSize(
          num_input_records / num_input_files,
          key_size, value_size, compression_ratio) * num_input_files;
  stats.total_output_bytes =
      EstimatedFileSize(
          num_output_records / num_output_files,
          key_size, value_size, compression_ratio) * num_output_files;
  stats.total_input_raw_key_bytes =
      num_input_records * (key_size + 8);
  stats.total_input_raw_value_bytes =
      num_input_records * value_size;

  stats.is_manual_compaction = is_manual;

  stats.num_records_replaced = num_records_replaced;

  CopyPrefix(smallest_key,
             CompactionJobStats::kMaxPrefixLength,
             &stats.smallest_output_key_prefix);
  CopyPrefix(largest_key,
             CompactionJobStats::kMaxPrefixLength,
             &stats.largest_output_key_prefix);

  return stats;
}

CompressionType GetAnyCompression() {
  if (Snappy_Supported()) {
    return kSnappyCompression;
  } else if (Zlib_Supported()) {
    return kZlibCompression;
  } else if (BZip2_Supported()) {
    return kBZip2Compression;
  } else if (LZ4_Supported()) {
    return kLZ4Compression;
  }
  return kNoCompression;
}

}  // namespace

TEST_F(CompactionJobStatsTest, CompactionJobStatsTest) {
  Random rnd(301);
  const int kBufSize = 100;
  char buf[kBufSize];
  uint64_t key_base = 100000000l;
  // Note: key_base must be multiple of num_keys_per_L0_file
  int num_keys_per_L0_file = 100;
  const int kTestScale = 8;
  const int kKeySize = 10;
  const int kValueSize = 1000;
  const double kCompressionRatio = 0.5;
  double compression_ratio = 1.0;
  uint64_t key_interval = key_base / num_keys_per_L0_file;

  // Whenever a compaction completes, this listener will try to
  // verify whether the returned CompactionJobStats matches
  // what we expect.  The expected CompactionJobStats is added
  // via AddExpectedStats().
  auto* stats_checker = new CompactionJobStatsChecker();
  Options options;
  options.listeners.emplace_back(stats_checker);
  options.create_if_missing = true;
  options.max_background_flushes = 0;
  // just enough setting to hold off auto-compaction.
  options.level0_file_num_compaction_trigger = kTestScale + 1;
  options.num_levels = 3;
  options.compression = kNoCompression;

  for (int test = 0; test < 2; ++test) {
    DestroyAndReopen(options);
    CreateAndReopenWithCF({"pikachu"}, options);

    // 1st Phase: generate "num_L0_files" L0 files.
    int num_L0_files = 0;
    for (uint64_t start_key = key_base;
                  start_key <= key_base * kTestScale;
                  start_key += key_base) {
      MakeTableWithKeyValues(
          &rnd, start_key, start_key + key_base - 1,
          kKeySize, kValueSize, key_interval,
          compression_ratio, 1);
      snprintf(buf, kBufSize, "%d", ++num_L0_files);
      ASSERT_EQ(std::string(buf), FilesPerLevel(1));
    }
    ASSERT_EQ(ToString(num_L0_files), FilesPerLevel(1));

    // 2nd Phase: perform L0 -> L1 compaction.
    int L0_compaction_count = 6;
    int count = 1;
    std::string smallest_key;
    std::string largest_key;
    for (uint64_t start_key = key_base;
         start_key <= key_base * L0_compaction_count;
         start_key += key_base, count++) {
      smallest_key = Key(start_key, 10);
      largest_key = Key(start_key + key_base - key_interval, 10);
      stats_checker->AddExpectedStats(
          NewManualCompactionJobStats(
              smallest_key, largest_key,
              1, 0, num_keys_per_L0_file,
              kKeySize, kValueSize,
              1, num_keys_per_L0_file,
              compression_ratio, 0));
      ASSERT_EQ(stats_checker->NumberOfUnverifiedStats(), 1U);
      TEST_Compact(0, 1, smallest_key, largest_key);
      snprintf(buf, kBufSize, "%d,%d", num_L0_files - count, count);
      ASSERT_EQ(std::string(buf), FilesPerLevel(1));
    }

    // compact two files into one in the last L0 -> L1 compaction
    int num_remaining_L0 = num_L0_files - L0_compaction_count;
    smallest_key = Key(key_base * (L0_compaction_count + 1), 10);
    largest_key = Key(key_base * (kTestScale + 1) - key_interval, 10);
    stats_checker->AddExpectedStats(
        NewManualCompactionJobStats(
            smallest_key, largest_key,
            num_remaining_L0,
            0, num_keys_per_L0_file * num_remaining_L0,
            kKeySize, kValueSize,
            1, num_keys_per_L0_file * num_remaining_L0,
            compression_ratio, 0));
    ASSERT_EQ(stats_checker->NumberOfUnverifiedStats(), 1U);
    TEST_Compact(0, 1, smallest_key, largest_key);

    int num_L1_files = num_L0_files - num_remaining_L0 + 1;
    num_L0_files = 0;
    snprintf(buf, kBufSize, "%d,%d", num_L0_files, num_L1_files);
    ASSERT_EQ(std::string(buf), FilesPerLevel(1));

    // 3rd Phase: generate sparse L0 files (wider key-range, same num of keys)
    int sparseness = 2;
    for (uint64_t start_key = key_base;
                  start_key <= key_base * kTestScale;
                  start_key += key_base * sparseness) {
      MakeTableWithKeyValues(
          &rnd, start_key, start_key + key_base * sparseness - 1,
          kKeySize, kValueSize,
          key_base * sparseness / num_keys_per_L0_file,
          compression_ratio, 1);
      snprintf(buf, kBufSize, "%d,%d", ++num_L0_files, num_L1_files);
      ASSERT_EQ(std::string(buf), FilesPerLevel(1));
    }

    // 4th Phase: perform L0 -> L1 compaction again, expect higher write amp
    for (uint64_t start_key = key_base;
         num_L0_files > 1;
         start_key += key_base * sparseness) {
      smallest_key = Key(start_key, 10);
      largest_key =
          Key(start_key + key_base * sparseness - key_interval, 10);
      stats_checker->AddExpectedStats(
          NewManualCompactionJobStats(
              smallest_key, largest_key,
              3, 2, num_keys_per_L0_file * 3,
              kKeySize, kValueSize,
              1, num_keys_per_L0_file * 2,  // 1/3 of the data will be updated.
              compression_ratio,
              num_keys_per_L0_file));
      ASSERT_EQ(stats_checker->NumberOfUnverifiedStats(), 1U);
      Compact(1, smallest_key, largest_key);
      snprintf(buf, kBufSize, "%d,%d",
          --num_L0_files, --num_L1_files);
      ASSERT_EQ(std::string(buf), FilesPerLevel(1));
    }

    // 5th Phase: Do a full compaction, which involves in two sub-compactions.
    // Here we expect to have 1 L0 files and 4 L1 files
    // In the first sub-compaction, we expect L0 compaction.
    smallest_key = Key(key_base, 10);
    largest_key = Key(key_base * (kTestScale + 1) - key_interval, 10);
    stats_checker->AddExpectedStats(
        NewManualCompactionJobStats(
            Key(key_base * (kTestScale + 1 - sparseness), 10), largest_key,
            2, 1, num_keys_per_L0_file * 3,
            kKeySize, kValueSize,
            1, num_keys_per_L0_file * 2,
            compression_ratio,
            num_keys_per_L0_file));
    ASSERT_EQ(stats_checker->NumberOfUnverifiedStats(), 1U);
    Compact(1, smallest_key, largest_key);
    ASSERT_EQ("0,4", FilesPerLevel(1));
    options.compression = GetAnyCompression();
    if (options.compression == kNoCompression) {
      break;
    }
    stats_checker->EnableCompression(true);
    compression_ratio = kCompressionRatio;
  }
  ASSERT_EQ(stats_checker->NumberOfUnverifiedStats(), 0U);
}

TEST_F(CompactionJobStatsTest, DeletionStatsTest) {
  Random rnd(301);
  uint64_t key_base = 100000l;
  // Note: key_base must be multiple of num_keys_per_L0_file
  int num_keys_per_L0_file = 20;
  const int kTestScale = 8;  // make sure this is even
  const int kKeySize = 10;
  const int kValueSize = 100;
  double compression_ratio = 1.0;
  uint64_t key_interval = key_base / num_keys_per_L0_file;
  uint64_t largest_key_num = key_base * (kTestScale + 1) - key_interval;
  uint64_t cutoff_key_num = key_base * (kTestScale / 2 + 1) - key_interval;
  const std::string smallest_key = Key(key_base - 10, kKeySize);
  const std::string largest_key = Key(largest_key_num + 10, kKeySize);

  // Whenever a compaction completes, this listener will try to
  // verify whether the returned CompactionJobStats matches
  // what we expect.
  auto* stats_checker = new CompactionJobDeletionStatsChecker();
  Options options;
  options.listeners.emplace_back(stats_checker);
  options.create_if_missing = true;
  options.max_background_flushes = 0;
  options.level0_file_num_compaction_trigger = kTestScale+1;
  options.num_levels = 3;
  options.compression = kNoCompression;
  options.max_bytes_for_level_multiplier = 2;

  DestroyAndReopen(options);
  CreateAndReopenWithCF({"pikachu"}, options);

  // Stage 1: Generate several L0 files and then send them to L2 by
  // using CompactRangeOptions and CompactRange(). These files will
  // have a strict subset of the keys from the full key-range
  for (uint64_t start_key = key_base;
                start_key <= key_base * kTestScale / 2;
                start_key += key_base) {
    MakeTableWithKeyValues(
        &rnd, start_key, start_key + key_base - 1,
        kKeySize, kValueSize, key_interval,
        compression_ratio, 1);
  }

  CompactRangeOptions cr_options;
  cr_options.change_level = true;
  cr_options.target_level = 2;
  db_->CompactRange(cr_options, handles_[1], nullptr, nullptr);
  ASSERT_GT(NumTableFilesAtLevel(2, 1), 0);

  // Stage 2: Generate files including keys from the entire key range
  for (uint64_t start_key = key_base;
                start_key <= key_base * kTestScale;
                start_key += key_base) {
    MakeTableWithKeyValues(
        &rnd, start_key, start_key + key_base - 1,
        kKeySize, kValueSize, key_interval,
        compression_ratio, 1);
  }

  // Send these L0 files to L1
  TEST_Compact(0, 1, smallest_key, largest_key);
  ASSERT_GT(NumTableFilesAtLevel(1, 1), 0);

  // Add a new record and flush so now there is a L0 file
  // with a value too (not just deletions from the next step)
  ASSERT_OK(Put(1, Key(key_base-6, kKeySize), "test"));
  ASSERT_OK(Flush(1));

  // Stage 3: Generate L0 files with some deletions so now
  // there are files with the same key range in L0, L1, and L2
  int deletion_interval = 3;
  CompactionJobStats first_compaction_stats;
  SelectivelyDeleteKeys(key_base, largest_key_num,
      key_interval, deletion_interval, kKeySize, cutoff_key_num,
      &first_compaction_stats, 1);

  stats_checker->AddExpectedStats(first_compaction_stats);

  // Stage 4: Trigger compaction and verify the stats
  TEST_Compact(0, 1, smallest_key, largest_key);
}

namespace {
int GetUniversalCompactionInputUnits(uint32_t num_flushes) {
  uint32_t compaction_input_units;
  for (compaction_input_units = 1;
       num_flushes >= compaction_input_units;
       compaction_input_units *= 2) {
    if ((num_flushes & compaction_input_units) != 0) {
      return compaction_input_units > 1 ? compaction_input_units : 0;
    }
  }
  return 0;
}
}  // namespace

TEST_F(CompactionJobStatsTest, UniversalCompactionTest) {
  Random rnd(301);
  uint64_t key_base = 100000000l;
  // Note: key_base must be multiple of num_keys_per_L0_file
  int num_keys_per_table = 100;
  const uint32_t kTestScale = 8;
  const int kKeySize = 10;
  const int kValueSize = 900;
  double compression_ratio = 1.0;
  uint64_t key_interval = key_base / num_keys_per_table;

  auto* stats_checker = new CompactionJobStatsChecker();
  Options options;
  options.listeners.emplace_back(stats_checker);
  options.create_if_missing = true;
  options.num_levels = 3;
  options.compression = kNoCompression;
  options.level0_file_num_compaction_trigger = 2;
  options.target_file_size_base = num_keys_per_table * 1000;
  options.compaction_style = kCompactionStyleUniversal;
  options.compaction_options_universal.size_ratio = 1;
  options.compaction_options_universal.max_size_amplification_percent = 1000;
  DestroyAndReopen(options);
  CreateAndReopenWithCF({"pikachu"}, options);

  // Generates the expected CompactionJobStats for each compaction
  for (uint32_t num_flushes = 2; num_flushes <= kTestScale; num_flushes++) {
    // Here we treat one newly flushed file as an unit.
    //
    // For example, if a newly flushed file is 100k, and a compaction has
    // 4 input units, then this compaction inputs 400k.
    uint32_t num_input_units = GetUniversalCompactionInputUnits(num_flushes);
    if (num_input_units == 0) {
      continue;
    }
    // The following statement determines the expected smallest key
    // based on whether it is a full compaction.  A full compaction only
    // happens when the number of flushes equals to the number of compaction
    // input runs.
    uint64_t smallest_key =
        (num_flushes == num_input_units) ?
            key_base : key_base * (num_flushes - 1);

    stats_checker->AddExpectedStats(
        NewManualCompactionJobStats(
            Key(smallest_key, 10),
            Key(smallest_key + key_base * num_input_units - key_interval, 10),
            num_input_units,
            num_input_units > 2 ? num_input_units / 2 : 0,
            num_keys_per_table * num_input_units,
            kKeySize, kValueSize,
            num_input_units,
            num_keys_per_table * num_input_units,
            1.0, 0, false));
  }
  ASSERT_EQ(stats_checker->NumberOfUnverifiedStats(), 4U);

  for (uint64_t start_key = key_base;
                start_key <= key_base * kTestScale;
                start_key += key_base) {
    MakeTableWithKeyValues(
        &rnd, start_key, start_key + key_base - 1,
        kKeySize, kValueSize, key_interval,
        compression_ratio, 1);
  }
  reinterpret_cast<DBImpl*>(db_)->TEST_WaitForCompact();
  ASSERT_EQ(stats_checker->NumberOfUnverifiedStats(), 0U);
}

}  // namespace rocksdb

int main(int argc, char** argv) {
  rocksdb::port::InstallStackTraceHandler();
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

#endif  // !ROCKSDB_LITE

#else

int main(int argc, char** argv) { return 0; }
#endif  // !defined(IOS_CROSS_COMPILE)


text:
=================================
Review Board 2.0.23 Release Notes
=================================

**Release date**: April 13, 2016

Upgrade Instructions
====================

To upgrade to Review Board 2.0.23, run::

    pip install ReviewBoard==2.0.23

or::

    easy_install ReviewBoard==2.0.23

Security Updates
================

This release fixes a self-XSS vulnerability in the editor fields of the
review request page.

This vulnerability requires the user to specifically enter or paste
specialized text designed to escape out of the current tag and to inject a
script. That script would run within the context of the page, much like the
browser's developer console would.

External users have no capability to trigger the injection of this text on
behalf of a user, and the vulnerability doesn't persist past the initial save
of the field.

New Features
============

* Added a field on the settings page for configuring the static media URL.

  This is useful if offloading the static media to a CDN or another
  dedicated server.

* Added information on the support level and status of any active support
  contracts in the administration dashboard.

* Added logging of access attempts on the API when the requesting user
  doesn't have access. (:bug:`3108`)

  This helps with security audits.

Extensions
==========

* Added compatibility with extensions packages as Python Wheels.

* Added support for extension-provided JavaScript unit tests.

  Extensions can now declare JavaScript unit test files in a ``"tests"``
  bundle. These tests will run automatically when accessing
  ``http://<server>/js-tests/extensions/`` on a development server.

  JSExtension classes aren't initialized automatically on this page. Test
  suites are responsible for initializing them if needed.

* Static media bundles can now include other bundles.

  This is specified by providing an ``include_bundles`` list in the bundle
  information. Note that this does not handle nesting of included bundles.
  Only the bundle being loaded can include other bundles.

Bug Fixes
=========

Diff Viewer
-----------

* Fixed filenames on interdiffs when the filenames have been renamed multiple
  times. (:bug:`4156`)

  If a file has been renamed more than once between a set of revisions, the
  filenames shown in the diff viewer would be incorrect. This has been changed
  to better represent the correct names.

  Patch by Adriano Arce.

* Fixed the display of tooltips for the diff complexity icons.

  Patch by Griffin Myers.

Review Requests
---------------

* Users configured as default reviewers are no longer added to review requests
  if they've been marked as inactive.

  Patch by Griffin Myers.

* Deleting file attachments that haven't been made public no longer keeps
  the files around in storage. (:bug:`4054`)

  Patch by Weijie Sun.

Git
---

* Fixed stale caches for file lookups after changing the Raw File URL mask
  for a repository. (:bug:`4051`)

  Patch by Kevin Chiu.

Perforce
--------

* Added compatibility with stunnel_ version 4 for Perforce.

  stunnel is used for securely sending traffic to another Perforce server.
  We've had support for stunnel version 3, which didn't work with version 4.

  We've added support for working with both version 3 and version 4. This
  shouldn't require any changes to existing setups.

* Fixed infinite loops attempting to find an unused port when using stunnel
  on MacOS X.

.. _stunnel: https://www.stunnel.org/

Subversion
----------

* Fixed Unicode errors with non-ASCII passwords on Subversion servers.
  (:bug:`4369`)

  Patch by Jim Hagan.

* Fixed fetching contents from Subversion repositories configured on
  Beanstalk_.

.. _Beanstalk: http://beanstalkapp.com/

Administration
--------------

* Fixed auto-updating the static media and uploaded media paths when changing
  the location of a site directory.

Contributors
============

* Adriano Arce
* Barret Rennie
* Christian Hammond
* David Trowbridge
* Griffin Myers
* Jim Hagan
* Kevin Chiu
* Weijie Sun


text:
unless collect_plugins(sp_hash_from_dirs(@sp_path))[0].include? 'private-view.rb'
  load_file File.join(File.dirname(__FILE__), 'sitemap.rb')
else

def sitemap(page = 'FrontPage')
  @map_path = []
  @map_traversed = []
  @map_str = ''

  return '' unless @db.exist?(page)
  @map_str = "
\n"
  sitemap_traverse(page)
  @map_str << "
\n"
end

def sitemap_traverse(page)
  return unless viewable?(page)
  info = @db.info(page)
  return if @map_path.index(page) or !info
  @map_path.push page

  @map_str << "
#{hiki_anchor(page.escape, "#{page_name(page)}")}
\n"

  unless @map_traversed.index(page)
    referer =  info[:references].sort.delete_if {|ref| !viewable?(ref)}
    if referer.size > 0
      @map_str << "
\n"
      referer.each do |r|
        sitemap_traverse(r)
      end
      @map_str << "
\n"
    end
    @map_traversed << page
  end
  @map_path.pop
end

export_plugin_methods(:sitemap)

end # of 'unless' on top of this file