<commit_before>/** @file

  A brief file description

  @section license License

  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */

#include "P_EventSystem.h" /* MAGIC_EDITING_TAG */
#include <sched.h>
#if TS_USE_HWLOC
#if HAVE_ALLOCA_H
#include <alloca.h>
#endif
#include <hwloc.h>
#endif
#include "ts/ink_defs.h"
#include "ts/hugepages.h"

EventType
EventProcessor::spawn_event_threads(int n_threads, const char *et_name, size_t stacksize)
{
  char thr_name[MAX_THREAD_NAME_LENGTH];
  EventType new_thread_group_id;
  int i;

  ink_release_assert(n_threads > 0);
  ink_release_assert((n_ethreads + n_threads) <= MAX_EVENT_THREADS);
  ink_release_assert(n_thread_groups < MAX_EVENT_TYPES);

  new_thread_group_id = (EventType)n_thread_groups;

  for (i = 0; i < n_threads; i++) {
    EThread *t                          = new EThread(REGULAR, n_ethreads + i);
    all_ethreads[n_ethreads + i]        = t;
    eventthread[new_thread_group_id][i] = t;
    t->set_event_type(new_thread_group_id);
  }

  n_threads_for_type[new_thread_group_id] = n_threads;
  for (i = 0; i < n_threads; i++) {
    snprintf(thr_name, MAX_THREAD_NAME_LENGTH, "[%s %d]", et_name, i);
    eventthread[new_thread_group_id][i]->start(thr_name, stacksize);
  }

  n_thread_groups++;
  n_ethreads += n_threads;
  Debug("iocore_thread", "Created thread group '%s' id %d with %d threads", et_name, new_thread_group_id, n_threads);

  return new_thread_group_id;
}

class EventProcessor eventProcessor;

int
EventProcessor::start(int n_event_threads, size_t stacksize)
{
  char thr_name[MAX_THREAD_NAME_LENGTH];
  int i;

  // do some sanity checking.
  static int started = 0;
  ink_release_assert(!started);
  ink_release_assert(n_event_threads > 0 && n_event_threads <= MAX_EVENT_THREADS);
  started = 1;

  n_ethreads      = n_event_threads;
  n_thread_groups = 1;

  // Make sure that our thread stack size is at least the minimum size
  stacksize = MAX(stacksize, INK_THREAD_STACK_MIN);

  // Make sure it is a multiple of our page size
  if (ats_hugepage_enabled()) {
    stacksize = INK_ALIGN(stacksize, ats_hugepage_size());
  } else {
    stacksize = INK_ALIGN(stacksize, ats_pagesize());
  }

  for (i = 0; i < n_event_threads; i++) {
    EThread *t = new EThread(REGULAR, i);
    if (i == 0) {
      ink_thread_setspecific(Thread::thread_data_key, t);
      Thread::get_hrtime_updated();
    }
    all_ethreads[i] = t;

    eventthread[ET_CALL][i] = t;
    t->set_event_type((EventType)ET_CALL);
  }
  n_threads_for_type[ET_CALL] = n_event_threads;

#if TS_USE_HWLOC
  int affinity = 1;
  REC_ReadConfigInteger(affinity, "proxy.config.exec_thread.affinity");
  hwloc_obj_t obj;
  hwloc_obj_type_t obj_type;
  int obj_count = 0;
  char *obj_name;

  switch (affinity) {
  case 4: // assign threads to logical processing units
// Older versions of libhwloc (eg. Ubuntu 10.04) don't have HWLOC_OBJ_PU.
#if HAVE_HWLOC_OBJ_PU
    obj_type = HWLOC_OBJ_PU;
    obj_name = (char *)"Logical Processor";
    break;
#endif
  case 3: // assign threads to real cores
    obj_type = HWLOC_OBJ_CORE;
    obj_name = (char *)"Core";
    break;
  case 1: // assign threads to NUMA nodes (often 1:1 with sockets)
    obj_type = HWLOC_OBJ_NODE;
    obj_name = (char *)"NUMA Node";
    if (hwloc_get_nbobjs_by_type(ink_get_topology(), obj_type) > 0) {
      break;
    }
  case 2: // assign threads to sockets
    obj_type = HWLOC_OBJ_SOCKET;
    obj_name = (char *)"Socket";
    break;
  default: // assign threads to the machine as a whole (a level below SYSTEM)
    obj_type = HWLOC_OBJ_MACHINE;
    obj_name = (char *)"Machine";
  }

  obj_count = hwloc_get_nbobjs_by_type(ink_get_topology(), obj_type);
  Debug("iocore_thread", "Affinity: %d %ss: %d PU: %d", affinity, obj_name, obj_count, ink_number_of_processors());

#endif
  for (i = 0; i < n_ethreads; i++) {
    ink_thread tid;
    if (i > 0) {
      snprintf(thr_name, MAX_THREAD_NAME_LENGTH, "[ET_NET %d]", i);
      tid = all_ethreads[i]->start(thr_name, stacksize);
    } else {
      tid = ink_thread_self();
    }
#if TS_USE_HWLOC
    if (obj_count > 0) {
      obj = hwloc_get_obj_by_type(ink_get_topology(), obj_type, i % obj_count);
#if HWLOC_API_VERSION >= 0x00010100
      int cpu_mask_len = hwloc_bitmap_snprintf(NULL, 0, obj->cpuset) + 1;
      char *cpu_mask   = (char *)alloca(cpu_mask_len);
      hwloc_bitmap_snprintf(cpu_mask, cpu_mask_len, obj->cpuset);
      Debug("iocore_thread", "EThread: %d %s: %d CPU Mask: %s", i, obj_name, obj->logical_index, cpu_mask);
#else
      Debug("iocore_thread", "EThread: %d %s: %d", i, obj_name, obj->logical_index);
#endif // HWLOC_API_VERSION
      hwloc_set_thread_cpubind(ink_get_topology(), tid, obj->cpuset, HWLOC_CPUBIND_STRICT);
    } else {
      Warning("hwloc returned an unexpected value -- CPU affinity disabled");
    }
#else
    (void)tid;
#endif // TS_USE_HWLOC
  }

  Debug("iocore_thread", "Created event thread group id %d with %d threads", ET_CALL, n_event_threads);
  return 0;
}

void
EventProcessor::shutdown()
{
}

Event *
EventProcessor::spawn_thread(Continuation *cont, const char *thr_name, size_t stacksize)
{
  ink_release_assert(n_dthreads < MAX_EVENT_THREADS);
  Event *e = eventAllocator.alloc();

  e->init(cont, 0, 0);
  all_dthreads[n_dthreads] = new EThread(DEDICATED, e);
  e->ethread               = all_dthreads[n_dthreads];
  e->mutex = e->continuation->mutex = all_dthreads[n_dthreads]->mutex;
  n_dthreads++;
  e->ethread->start(thr_name, stacksize);

  return e;
}
<commit_msg>TS-4806: Allocate thread stacks on corresponding NUMA nodes.<commit_after>/** @file

  A brief file description

  @section license License

  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */

#include "P_EventSystem.h" /* MAGIC_EDITING_TAG */
#include <sched.h>
#if TS_USE_HWLOC
#if HAVE_ALLOCA_H
#include <alloca.h>
#endif
#include <hwloc.h>
#endif
#include "ts/ink_defs.h"
#include "ts/hugepages.h"

EventType
EventProcessor::spawn_event_threads(int n_threads, const char *et_name, size_t stacksize)
{
  char thr_name[MAX_THREAD_NAME_LENGTH];
  EventType new_thread_group_id;
  int i;

  ink_release_assert(n_threads > 0);
  ink_release_assert((n_ethreads + n_threads) <= MAX_EVENT_THREADS);
  ink_release_assert(n_thread_groups < MAX_EVENT_TYPES);

  new_thread_group_id = (EventType)n_thread_groups;

  for (i = 0; i < n_threads; i++) {
    EThread *t                          = new EThread(REGULAR, n_ethreads + i);
    all_ethreads[n_ethreads + i]        = t;
    eventthread[new_thread_group_id][i] = t;
    t->set_event_type(new_thread_group_id);
  }

  n_threads_for_type[new_thread_group_id] = n_threads;
  for (i = 0; i < n_threads; i++) {
    snprintf(thr_name, MAX_THREAD_NAME_LENGTH, "[%s %d]", et_name, i);
    eventthread[new_thread_group_id][i]->start(thr_name, stacksize);
  }

  n_thread_groups++;
  n_ethreads += n_threads;
  Debug("iocore_thread", "Created thread group '%s' id %d with %d threads", et_name, new_thread_group_id, n_threads);

  return new_thread_group_id;
}

class EventProcessor eventProcessor;

int
EventProcessor::start(int n_event_threads, size_t stacksize)
{
  char thr_name[MAX_THREAD_NAME_LENGTH];
  int i;
  void *stack = NULL;

  // do some sanity checking.
  static int started = 0;
  ink_release_assert(!started);
  ink_release_assert(n_event_threads > 0 && n_event_threads <= MAX_EVENT_THREADS);
  started = 1;

  n_ethreads      = n_event_threads;
  n_thread_groups = 1;

  // Make sure that our thread stack size is at least the minimum size
  stacksize = MAX(stacksize, INK_THREAD_STACK_MIN);

  // Make sure it is a multiple of our page size
  if (ats_hugepage_enabled()) {
    stacksize = INK_ALIGN(stacksize, ats_hugepage_size());
  } else {
    stacksize = INK_ALIGN(stacksize, ats_pagesize());
  }

  for (i = 0; i < n_event_threads; i++) {
    EThread *t = new EThread(REGULAR, i);
    if (i == 0) {
      ink_thread_setspecific(Thread::thread_data_key, t);
      Thread::get_hrtime_updated();
    }
    all_ethreads[i] = t;

    eventthread[ET_CALL][i] = t;
    t->set_event_type((EventType)ET_CALL);
  }
  n_threads_for_type[ET_CALL] = n_event_threads;

#if TS_USE_HWLOC
  int affinity = 1;
  REC_ReadConfigInteger(affinity, "proxy.config.exec_thread.affinity");
  hwloc_obj_t obj;
  hwloc_obj_type_t obj_type;
  int obj_count = 0;
  char *obj_name;

  switch (affinity) {
  case 4: // assign threads to logical processing units
// Older versions of libhwloc (eg. Ubuntu 10.04) don't have HWLOC_OBJ_PU.
#if HAVE_HWLOC_OBJ_PU
    obj_type = HWLOC_OBJ_PU;
    obj_name = (char *)"Logical Processor";
    break;
#endif
  case 3: // assign threads to real cores
    obj_type = HWLOC_OBJ_CORE;
    obj_name = (char *)"Core";
    break;
  case 1: // assign threads to NUMA nodes (often 1:1 with sockets)
    obj_type = HWLOC_OBJ_NODE;
    obj_name = (char *)"NUMA Node";
    if (hwloc_get_nbobjs_by_type(ink_get_topology(), obj_type) > 0) {
      break;
    }
  case 2: // assign threads to sockets
    obj_type = HWLOC_OBJ_SOCKET;
    obj_name = (char *)"Socket";
    break;
  default: // assign threads to the machine as a whole (a level below SYSTEM)
    obj_type = HWLOC_OBJ_MACHINE;
    obj_name = (char *)"Machine";
  }

  obj_count = hwloc_get_nbobjs_by_type(ink_get_topology(), obj_type);
  Debug("iocore_thread", "Affinity: %d %ss: %d PU: %d", affinity, obj_name, obj_count, ink_number_of_processors());

#endif
  for (i = 0; i < n_ethreads; i++) {
    ink_thread tid;

#if TS_USE_HWLOC
    if (obj_count > 0) {
      obj = hwloc_get_obj_by_type(ink_get_topology(), obj_type, i % obj_count);
#if HWLOC_API_VERSION >= 0x00010100
      int cpu_mask_len = hwloc_bitmap_snprintf(NULL, 0, obj->cpuset) + 1;
      char *cpu_mask   = (char *)alloca(cpu_mask_len);
      hwloc_bitmap_snprintf(cpu_mask, cpu_mask_len, obj->cpuset);
      Debug("iocore_thread", "EThread: %d %s: %d CPU Mask: %s", i, obj_name, obj->logical_index, cpu_mask);
#else
      Debug("iocore_thread", "EThread: %d %s: %d", i, obj_name, obj->logical_index);
#endif // HWLOC_API_VERSION
    }
#endif // TS_USE_HWLOC

    if (i > 0) {
      snprintf(thr_name, MAX_THREAD_NAME_LENGTH, "[ET_NET %d]", i);
#if TS_USE_HWLOC
      if (obj_count > 0) {
        hwloc_nodeset_t nodeset = hwloc_bitmap_alloc();

        hwloc_cpuset_to_nodeset(ink_get_topology(), obj->cpuset, nodeset);

        if (hwloc_get_nbobjs_inside_cpuset_by_type(ink_get_topology(), obj->cpuset, HWLOC_OBJ_NODE) == 1) {
          stack = hwloc_alloc_membind_nodeset(ink_get_topology(), stacksize, nodeset, HWLOC_MEMBIND_BIND, 0);
        } else if (hwloc_get_nbobjs_inside_cpuset_by_type(ink_get_topology(), obj->cpuset, HWLOC_OBJ_NODE) > 1) {
          stack = hwloc_alloc_membind_nodeset(ink_get_topology(), stacksize, nodeset, HWLOC_MEMBIND_INTERLEAVE, 0);
        } else {
          stack = NULL;
        }

        hwloc_bitmap_free(nodeset);
      }
#endif // TS_USE_HWLOC
      tid = all_ethreads[i]->start(thr_name, stacksize, NULL, stack);
    } else {
      // We should stop using this thread like this and create a new one and have the master thread just join all these and block
      // indefinitely.
      tid = ink_thread_self();
    }

#if TS_USE_HWLOC
    if (obj_count > 0) {
      hwloc_set_thread_cpubind(ink_get_topology(), tid, obj->cpuset, HWLOC_CPUBIND_STRICT);
    } else {
      Warning("hwloc returned an unexpected value -- CPU affinity disabled");
    }
#else
    (void)tid;
#endif // TS_USE_HWLOC
  }

  Debug("iocore_thread", "Created event thread group id %d with %d threads", ET_CALL, n_event_threads);
  return 0;
}

void
EventProcessor::shutdown()
{
}

Event *
EventProcessor::spawn_thread(Continuation *cont, const char *thr_name, size_t stacksize)
{
  ink_release_assert(n_dthreads < MAX_EVENT_THREADS);
  Event *e = eventAllocator.alloc();

  e->init(cont, 0, 0);
  all_dthreads[n_dthreads] = new EThread(DEDICATED, e);
  e->ethread               = all_dthreads[n_dthreads];
  e->mutex = e->continuation->mutex = all_dthreads[n_dthreads]->mutex;
  n_dthreads++;
  e->ethread->start(thr_name, stacksize);

  return e;
}
<|endoftext|>


<commit_before>//
//  SkipGramCpp.cpp
//  dopamine
//
//  Created by 佐原 瑠能 on 2017/05/31.
//  Copyright © 2017年 Runo. All rights reserved.
//

#include <cstring>
#include <cmath>
#include <random>

extern "C" {

#include "FloatBufferCpp.hpp"
#include "SkipGramCpp.hpp"

#define EXPTABLE_SIZE		1000
#define EXPTABLE_INPUTMAX	6.0f

float* _SkipGram_ExpTable = NULL;

void _SkipGram_GlobalInit() {

	// exp/(exp+1)の計算テーブル
	if (_SkipGram_ExpTable == nullptr) {
		_SkipGram_ExpTable = (float*)malloc(EXPTABLE_SIZE * sizeof(float));
		for (int tableIndex = 0; tableIndex < EXPTABLE_SIZE; tableIndex++) {
			float input = (((float)tableIndex) / ((float)EXPTABLE_SIZE)) * (EXPTABLE_INPUTMAX * 2.0f) - EXPTABLE_INPUTMAX;
			float val = expf(input);
			_SkipGram_ExpTable[tableIndex] = val / (val + 1.0f);
		}
	}

}

void _SkipGram_TrainInit(int* itemSequenceBuffer, int itemSequenceBufferLength, int* itemSequenceOffsetArray, int* itemSequencesCount, float* itemNegLotteryInfoArray, int* itemsCount) {

	int maxItemSequencesCount = *itemSequencesCount;
	int maxItemsCount = *itemsCount;

	if (maxItemSequencesCount == 0 || maxItemsCount == 0) {
		*itemSequencesCount = 0;
		*itemsCount = 0;
		return;
	}

	int maxItemIndex = 0;
	memset(itemNegLotteryInfoArray, 0, maxItemsCount * sizeof (float));
	itemSequenceOffsetArray[0] = 0;
	int curItemSequenceIndex = 1;

	// バッファーを列挙する
	int* itemSequenceHead = itemSequenceBuffer;
	int* itemSequenceBufferEnd = itemSequenceBuffer + itemSequenceBufferLength;
	for (; itemSequenceHead < itemSequenceBufferEnd; itemSequenceHead++) {

		int itemIndex = *itemSequenceHead;

		// シーケンスの終了チェック
		if (itemIndex < 0) {

			// 最後のシーケンス
			if (itemSequenceHead + 1 == itemSequenceBufferEnd
				|| maxItemSequencesCount == curItemSequenceIndex) {
				break;
			}

			itemSequenceOffsetArray[curItemSequenceIndex] = (int)(itemSequenceHead - itemSequenceBuffer) + 1;
			curItemSequenceIndex++;
		}

		// アイテムIDの上限チェック
		if (itemIndex >= maxItemsCount) {
			break;
		}

		// 最大値更新
		if (itemIndex > maxItemIndex) {
			maxItemIndex = itemIndex;
		}

		// アイテムをカウント
		itemNegLotteryInfoArray[itemIndex] += 1.0f;
	}

	// 結果を設定
	*itemSequencesCount = curItemSequenceIndex;
	int actualItemsCount = maxItemIndex + 1;
	*itemsCount = actualItemsCount;

	// 抽選データを準備
	float sum = 0.0f;
	float* itemNegLotteryInfoEnd = itemNegLotteryInfoArray + actualItemsCount;
	for (float* head = itemNegLotteryInfoArray; head < itemNegLotteryInfoEnd; head++) {
		*head = powf(*head, 0.75f);
		sum += *head;
	}
	float sumInv = 1.0f / sum;
	float acc = 0.0f;
	for (float* head = itemNegLotteryInfoArray; head < itemNegLotteryInfoEnd; head++) {
		acc += *head * sumInv;
		*head = acc;
	}
}

inline int _SkipGram_RandomNegativeItemIndex(float* itemNegLotteryInfoArray, int itemsCount, float randVal) {

	int minIndex = 0;
	int maxIndex = itemsCount - 1;
	while (minIndex < maxIndex) {
		int testIndex = (minIndex + maxIndex) / 2;
		float testVal = itemNegLotteryInfoArray[testIndex];
		if (randVal < testVal) {
			maxIndex = testIndex - 1;
		} else {
			minIndex = testIndex + 1;
		}
	}
	return maxIndex;
}

inline float _SkipGram_ExpTableOutput(float input) {

	int tableIndex = (input + EXPTABLE_INPUTMAX) * (((float)EXPTABLE_SIZE) / EXPTABLE_INPUTMAX / 2.0f);
	if (tableIndex < 0)
		return 0.0f;
	if (tableIndex >= EXPTABLE_SIZE)
		return 1.0f;

	return _SkipGram_ExpTable[tableIndex];
}

void _SkipGram_TrainIterate(int* itemSequenceBuffer, int* itemSequenceOffsetArray, int itemSequencesCount, float* itemNegLotteryInfoArray, int itemsCount, int itemVectorSize, float* weightBuffer, float* negWeightBuffer, float* tempItemVector, int windowSize, int negativeSamplingCount, float learningRate, int iterationsCount) {

	std::random_device dev;
	std::mt19937 gen(dev());
	std::uniform_real_distribution<float> dist(0.0f, itemNegLotteryInfoArray[itemsCount - 1]);

	while (iterationsCount--) { // For each iteration.
		for (int itemSequenceIndex = 0; itemSequenceIndex < itemSequencesCount; itemSequenceIndex++) { // For each sequence.

			int* itemSequenceStart = itemSequenceBuffer + itemSequenceOffsetArray[itemSequenceIndex];
			for (int* headItem = itemSequenceStart; ; headItem++) { // For each item.
				int itemIndex = *headItem;
				if (itemIndex < 0) {
					break; // End of sequence.
				}

				int* headRelated = headItem - windowSize;
				int* headRelatedEnd = headItem + windowSize;
				if (headRelated < itemSequenceStart) {
					headRelated = itemSequenceStart;
				}

				for (; headRelated <= headRelatedEnd; headRelated++) { // For each related item.
					int relatedItemIndex = *headRelated;
					if (relatedItemIndex == itemIndex) {
						continue; // Skip center item.
					}
					if (relatedItemIndex < 0) {
						break; // End of sequence.
					}

					memset(tempItemVector, 0, itemVectorSize * sizeof (float));

					float* relatedVector = weightBuffer + (itemVectorSize * relatedItemIndex);

					// Positive sampling, then negative sampling.
					float label;
					int targetItemIndex;
					for (int negativeSamplingIndex = -1; negativeSamplingIndex < negativeSamplingCount; negativeSamplingIndex++) { // For each (positive/negative) sampling.

						if (negativeSamplingIndex == -1) {
							targetItemIndex = itemIndex;
							label = 1.0f;
						} else {
							targetItemIndex = _SkipGram_RandomNegativeItemIndex(itemNegLotteryInfoArray, itemsCount, dist(gen));
							if (targetItemIndex == itemIndex) {
								continue;
							}
							label = 0.0f;
						}

						float* targetVector = negWeightBuffer + (itemVectorSize * targetItemIndex);

						float dotProduct = _FloatBuffer_DotProduct(relatedVector, targetVector, itemVectorSize);

#if 0 // 計算版
						float expDotProduct = expf(dotProduct);
						float delta = (label - (expDotProduct / (expDotProduct + 1.0f))) * learningRate;
#else // テーブル版
						float delta = (label - _SkipGram_ExpTableOutput(dotProduct)) * learningRate;
#endif

#if 0 // BLAS版
						_FloatBuffer_AddScaled(tempItemVector, targetVector, delta, itemVectorSize, itemVectorSize);
						_FloatBuffer_AddScaled(targetVector, relatedVector, delta, itemVectorSize, itemVectorSize);
#endif
#if 1 // ポインタ版
						float* headTempItemVector = tempItemVector;
						float* headRelatedVector = relatedVector;
						for (float* targetVectorEnd = targetVector + itemVectorSize; targetVector < targetVectorEnd; targetVector++) {
							*headTempItemVector += delta * *targetVector;
							*targetVector += delta * *headRelatedVector;

							headTempItemVector++;
							headRelatedVector++;
						}
#endif

					} // For each (positive/negative) sampling.

					FloatBuffer_Add(relatedVector, tempItemVector, itemVectorSize, itemVectorSize);

				} // For each related item.

			} // For each item.

		} // For each sequence.

	} // For each iteration.

}

}
<commit_msg>インデックスクリップ<commit_after>//
//  SkipGramCpp.cpp
//  dopamine
//
//  Created by 佐原 瑠能 on 2017/05/31.
//  Copyright © 2017年 Runo. All rights reserved.
//

#include <cstring>
#include <cmath>
#include <random>

extern "C" {

#include "FloatBufferCpp.hpp"
#include "SkipGramCpp.hpp"

#define EXPTABLE_SIZE		1000
#define EXPTABLE_INPUTMAX	6.0f

float* _SkipGram_ExpTable = NULL;

void _SkipGram_GlobalInit() {

	// exp/(exp+1)の計算テーブル
	if (_SkipGram_ExpTable == nullptr) {
		_SkipGram_ExpTable = (float*)malloc(EXPTABLE_SIZE * sizeof(float));
		for (int tableIndex = 0; tableIndex < EXPTABLE_SIZE; tableIndex++) {
			float input = (((float)tableIndex) / ((float)EXPTABLE_SIZE)) * (EXPTABLE_INPUTMAX * 2.0f) - EXPTABLE_INPUTMAX;
			float val = expf(input);
			_SkipGram_ExpTable[tableIndex] = val / (val + 1.0f);
		}
	}

}

void _SkipGram_TrainInit(int* itemSequenceBuffer, int itemSequenceBufferLength, int* itemSequenceOffsetArray, int* itemSequencesCount, float* itemNegLotteryInfoArray, int* itemsCount) {

	int maxItemSequencesCount = *itemSequencesCount;
	int maxItemsCount = *itemsCount;

	if (maxItemSequencesCount == 0 || maxItemsCount == 0) {
		*itemSequencesCount = 0;
		*itemsCount = 0;
		return;
	}

	int maxItemIndex = 0;
	memset(itemNegLotteryInfoArray, 0, maxItemsCount * sizeof (float));
	itemSequenceOffsetArray[0] = 0;
	int curItemSequenceIndex = 1;

	// バッファーを列挙する
	int* itemSequenceHead = itemSequenceBuffer;
	int* itemSequenceBufferEnd = itemSequenceBuffer + itemSequenceBufferLength;
	for (; itemSequenceHead < itemSequenceBufferEnd; itemSequenceHead++) {

		int itemIndex = *itemSequenceHead;

		// シーケンスの終了チェック
		if (itemIndex < 0) {

			// 最後のシーケンス
			if (itemSequenceHead + 1 == itemSequenceBufferEnd
				|| maxItemSequencesCount == curItemSequenceIndex) {
				break;
			}

			itemSequenceOffsetArray[curItemSequenceIndex] = (int)(itemSequenceHead - itemSequenceBuffer) + 1;
			curItemSequenceIndex++;
		}

		// アイテムIDの上限チェック
		if (itemIndex >= maxItemsCount) {
			break;
		}

		// 最大値更新
		if (itemIndex > maxItemIndex) {
			maxItemIndex = itemIndex;
		}

		// アイテムをカウント
		itemNegLotteryInfoArray[itemIndex] += 1.0f;
	}

	// 結果を設定
	*itemSequencesCount = curItemSequenceIndex;
	int actualItemsCount = maxItemIndex + 1;
	*itemsCount = actualItemsCount;

	// 抽選データを準備
	float sum = 0.0f;
	float* itemNegLotteryInfoEnd = itemNegLotteryInfoArray + actualItemsCount;
	for (float* head = itemNegLotteryInfoArray; head < itemNegLotteryInfoEnd; head++) {
		*head = powf(*head, 0.75f);
		sum += *head;
	}
	float sumInv = 1.0f / sum;
	float acc = 0.0f;
	for (float* head = itemNegLotteryInfoArray; head < itemNegLotteryInfoEnd; head++) {
		acc += *head * sumInv;
		*head = acc;
	}
}

inline int _SkipGram_RandomNegativeItemIndex(float* itemNegLotteryInfoArray, int itemsCount, float randVal) {

	int minIndex = 0;
	int maxIndex = itemsCount - 1;
	while (minIndex < maxIndex) {
		int testIndex = (minIndex + maxIndex) / 2;
		float testVal = itemNegLotteryInfoArray[testIndex];
		if (randVal < testVal) {
			maxIndex = testIndex - 1;
		} else {
			minIndex = testIndex + 1;
		}
	}

	if (maxIndex < 0)
		return 0;
	if (maxIndex >= itemsCount)
		return itemsCount - 1;
	return maxIndex;
}

inline float _SkipGram_ExpTableOutput(float input) {

	int tableIndex = (input + EXPTABLE_INPUTMAX) * (((float)EXPTABLE_SIZE) / EXPTABLE_INPUTMAX / 2.0f);
	if (tableIndex < 0)
		return 0.0f;
	if (tableIndex >= EXPTABLE_SIZE)
		return 1.0f;

	return _SkipGram_ExpTable[tableIndex];
}

void _SkipGram_TrainIterate(int* itemSequenceBuffer, int* itemSequenceOffsetArray, int itemSequencesCount, float* itemNegLotteryInfoArray, int itemsCount, int itemVectorSize, float* weightBuffer, float* negWeightBuffer, float* tempItemVector, int windowSize, int negativeSamplingCount, float learningRate, int iterationsCount) {

	std::random_device dev;
	std::mt19937 gen(dev());
	std::uniform_real_distribution<float> dist(0.0f, itemNegLotteryInfoArray[itemsCount - 1]);

	while (iterationsCount--) { // For each iteration.
		for (int itemSequenceIndex = 0; itemSequenceIndex < itemSequencesCount; itemSequenceIndex++) { // For each sequence.

			int* itemSequenceStart = itemSequenceBuffer + itemSequenceOffsetArray[itemSequenceIndex];
			for (int* headItem = itemSequenceStart; ; headItem++) { // For each item.
				int itemIndex = *headItem;
				if (itemIndex < 0) {
					break; // End of sequence.
				}

				int* headRelated = headItem - windowSize;
				int* headRelatedEnd = headItem + windowSize;
				if (headRelated < itemSequenceStart) {
					headRelated = itemSequenceStart;
				}

				for (; headRelated <= headRelatedEnd; headRelated++) { // For each related item.
					int relatedItemIndex = *headRelated;
					if (relatedItemIndex == itemIndex) {
						continue; // Skip center item.
					}
					if (relatedItemIndex < 0) {
						break; // End of sequence.
					}

					memset(tempItemVector, 0, itemVectorSize * sizeof (float));

					float* relatedVector = weightBuffer + (itemVectorSize * relatedItemIndex);

					// Positive sampling, then negative sampling.
					float label;
					int targetItemIndex;
					for (int negativeSamplingIndex = -1; negativeSamplingIndex < negativeSamplingCount; negativeSamplingIndex++) { // For each (positive/negative) sampling.

						if (negativeSamplingIndex == -1) {
							targetItemIndex = itemIndex;
							label = 1.0f;
						} else {
							targetItemIndex = _SkipGram_RandomNegativeItemIndex(itemNegLotteryInfoArray, itemsCount, dist(gen));
							if (targetItemIndex == itemIndex) {
								continue;
							}
							label = 0.0f;
						}

						float* targetVector = negWeightBuffer + (itemVectorSize * targetItemIndex);

						float dotProduct = _FloatBuffer_DotProduct(relatedVector, targetVector, itemVectorSize);

#if 0 // 計算版
						float expDotProduct = expf(dotProduct);
						float delta = (label - (expDotProduct / (expDotProduct + 1.0f))) * learningRate;
#else // テーブル版
						float delta = (label - _SkipGram_ExpTableOutput(dotProduct)) * learningRate;
#endif

#if 0 // BLAS版
						_FloatBuffer_AddScaled(tempItemVector, targetVector, delta, itemVectorSize, itemVectorSize);
						_FloatBuffer_AddScaled(targetVector, relatedVector, delta, itemVectorSize, itemVectorSize);
#endif
#if 1 // ポインタ版
						float* headTempItemVector = tempItemVector;
						float* headRelatedVector = relatedVector;
						for (float* targetVectorEnd = targetVector + itemVectorSize; targetVector < targetVectorEnd; targetVector++) {
							*headTempItemVector += delta * *targetVector;
							*targetVector += delta * *headRelatedVector;

							headTempItemVector++;
							headRelatedVector++;
						}
#endif

					} // For each (positive/negative) sampling.

					FloatBuffer_Add(relatedVector, tempItemVector, itemVectorSize, itemVectorSize);

				} // For each related item.

			} // For each item.

		} // For each sequence.

	} // For each iteration.

}

}
<|endoftext|>


<commit_before>/// @file
/// @author uentity
/// @date 28.06.2018
/// @brief BS tree links serialization code
/// @copyright
/// This Source Code Form is subject to the terms of the Mozilla Public License,
/// v. 2.0. If a copy of the MPL was not distributed with this file,
/// You can obtain one at https://mozilla.org/MPL/2.0/

#include <bs/serialize/serialize.h>
#include <bs/serialize/tree.h>
#include <bs/serialize/boost_uuid.h>

#include "tree_impl.h"
#include "../tree/link_actor.h"
#include "../tree/hard_link.h"
#include "../tree/fusion_link_actor.h"
#include "../tree/map_engine.h"

#include <cereal/types/chrono.hpp>

using namespace cereal;
using namespace blue_sky;
using blue_sky::detail::shared;

/*-----------------------------------------------------------------------------
 *  inode
 *-----------------------------------------------------------------------------*/
BSS_FCN_BEGIN(save, tree::inode)
	ar(
		make_nvp("flags", t.flags),
		make_nvp("u", t.u),
		make_nvp("g", t.g),
		make_nvp("o", t.o),
		make_nvp("mod_time", t.mod_time),
		make_nvp("owner", t.owner),
		make_nvp("group", t.group)
	);
BSS_FCN_END

BSS_FCN_BEGIN(load, tree::inode)
	tree::inode::Flags flags;
	ar(make_nvp("flags", flags));
	t.flags = flags;
	std::uint8_t r;
	ar(make_nvp("u", r));
	t.u = r;
	ar(make_nvp("g", r));
	t.g = r;
	ar(make_nvp("o", r));
	t.o = r;
	ar(
		make_nvp("mod_time", t.mod_time),
		make_nvp("owner", t.owner),
		make_nvp("group", t.group)
	);
BSS_FCN_END

BSS_FCN_EXPORT(save, tree::inode)
BSS_FCN_EXPORT(load, tree::inode)

/*-----------------------------------------------------------------------------
 *  link_impl
 *-----------------------------------------------------------------------------*/
BSS_FCN_BEGIN(serialize, tree::link_impl)
	// [NOTE] intentionally do net serialize owner, it will be set up when parent node is loaded
	ar(
		make_nvp("id", t.id_),
		make_nvp("name", t.name_),
		make_nvp("flags", t.flags_)
	);
BSS_FCN_END

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::engine::impl, tree::link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::link_impl, "link")
BSS_FCN_EXPORT(serialize, tree::link_impl)

///////////////////////////////////////////////////////////////////////////////
//  ilink_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::ilink_impl)
	// serialize inode
	ar(make_nvp("inode", t.inode_));

	serialize<tree::link_impl>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::ilink_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::link_impl, tree::ilink_impl)

///////////////////////////////////////////////////////////////////////////////
//  hard_link_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::hard_link_impl)
	if constexpr(Archive::is_saving::value) {
		ar( make_nvp("data", t.data_) );
	}
	else {
		// load data with deferred 2nd trial
		ar(defer_failed(
			t.data_,
			[&t](auto obj) { t.set_data(std::move(obj)); },
			PtrInitTrigger::SuccessAndRetry
		));
	}

	serialize<tree::ilink_impl>::go(ar, t, version);
	//ar( make_nvp("linkbase", base_class<tree::ilink_impl>(&t)) );
BSS_FCN_INL_END(serialize, tree::hard_link_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::ilink_impl, tree::hard_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::hard_link_impl, "hard_link")

///////////////////////////////////////////////////////////////////////////////
//  weak_link_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::weak_link_impl)
	if constexpr(Archive::is_saving::value) {
		ar( make_nvp("data", t.data_.lock()) );
	}
	else {
		auto unused = sp_obj{};
		ar(defer_failed(
			unused,
			[&t](auto obj) { t.set_data(std::move(obj)); },
			PtrInitTrigger::SuccessAndRetry
		));
	}

	serialize<tree::ilink_impl>::go(ar, t, version);
	//ar( make_nvp("linkbase", base_class<tree::ilink_impl>(&t)) );
BSS_FCN_INL_END(serialize, tree::weak_link_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::ilink_impl, tree::weak_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::weak_link_impl, "weak_link")

///////////////////////////////////////////////////////////////////////////////
//  sym_link_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::sym_link_impl)
	ar(make_nvp("path", t.path_));

	serialize<tree::link_impl>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::sym_link_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::link_impl, tree::sym_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::sym_link_impl, "sym_link")

///////////////////////////////////////////////////////////////////////////////
//  fusion_link_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::fusion_link_impl)
	if constexpr(Archive::is_saving::value) {
		ar( make_nvp("bridge", t.bridge_) );
	}
	else {
		// load bridge with deferred trial
		ar(defer_failed(
			t.bridge_,
			[&t](auto B) { t.bridge_ = std::move(B); },
			PtrInitTrigger::SuccessAndRetry
		));
	}

	serialize<tree::ilink_impl>::go(ar, t, version);
	//ar( make_nvp("linkbase", base_class<tree::ilink_impl>(&t)) );
BSS_FCN_INL_END(serialize, tree::fusion_link_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::ilink_impl, tree::fusion_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::fusion_link_impl, "fusion_link")

///////////////////////////////////////////////////////////////////////////////
//  map_link - not serialized
//
CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::link_impl, tree::map_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::map_link_impl, "map_link")

/*-----------------------------------------------------------------------------
 *  link
 *-----------------------------------------------------------------------------*/
BSS_FCN_BEGIN(serialize, tree::link)
	// map links are not serialized at all
	if(t.type_id() == tree::map_link::type_id_()) return;

	// [NOTE] intentionally do net serialize owner, it will be set up when parent node is loaded
	if constexpr(Archive::is_saving::value) {
		// for nil links save empty impl pointer
		auto Limpl = t ? t.pimpl_ : tree::sp_limpl{};
		ar(make_nvp("link", Limpl));
	}
	else {
		ar(make_nvp("link", t.pimpl_));
		if(!t.pimpl_) t = tree::link{};
		// ID is ready, we can start internal actor
		t.start_engine();
		// if pointee is a node, correct it's handle
		t.pimpl()->propagate_handle();
	}
BSS_FCN_END

BSS_FCN_EXPORT(serialize, tree::link)

BSS_REGISTER_DYNAMIC_INIT(link)
<commit_msg>serial: disable serialization of inodes<commit_after>/// @file
/// @author uentity
/// @date 28.06.2018
/// @brief BS tree links serialization code
/// @copyright
/// This Source Code Form is subject to the terms of the Mozilla Public License,
/// v. 2.0. If a copy of the MPL was not distributed with this file,
/// You can obtain one at https://mozilla.org/MPL/2.0/

#include <bs/serialize/serialize.h>
#include <bs/serialize/tree.h>
#include <bs/serialize/boost_uuid.h>

#include "tree_impl.h"
#include "../tree/link_actor.h"
#include "../tree/hard_link.h"
#include "../tree/fusion_link_actor.h"
#include "../tree/map_engine.h"

#include <cereal/types/chrono.hpp>

using namespace cereal;
using namespace blue_sky;
using blue_sky::detail::shared;

/*-----------------------------------------------------------------------------
 *  inode
 *-----------------------------------------------------------------------------*/
BSS_FCN_BEGIN(save, tree::inode)
	ar(
		make_nvp("flags", t.flags),
		make_nvp("u", t.u),
		make_nvp("g", t.g),
		make_nvp("o", t.o),
		make_nvp("mod_time", t.mod_time),
		make_nvp("owner", t.owner),
		make_nvp("group", t.group)
	);
BSS_FCN_END

BSS_FCN_BEGIN(load, tree::inode)
	tree::inode::Flags flags;
	ar(make_nvp("flags", flags));
	t.flags = flags;
	std::uint8_t r;
	ar(make_nvp("u", r));
	t.u = r;
	ar(make_nvp("g", r));
	t.g = r;
	ar(make_nvp("o", r));
	t.o = r;
	ar(
		make_nvp("mod_time", t.mod_time),
		make_nvp("owner", t.owner),
		make_nvp("group", t.group)
	);
BSS_FCN_END

BSS_FCN_EXPORT(save, tree::inode)
BSS_FCN_EXPORT(load, tree::inode)

/*-----------------------------------------------------------------------------
 *  link_impl
 *-----------------------------------------------------------------------------*/
BSS_FCN_BEGIN(serialize, tree::link_impl)
	// [NOTE] intentionally do net serialize owner, it will be set up when parent node is loaded
	ar(
		make_nvp("id", t.id_),
		make_nvp("name", t.name_),
		make_nvp("flags", t.flags_)
	);
BSS_FCN_END

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::engine::impl, tree::link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::link_impl, "link")
BSS_FCN_EXPORT(serialize, tree::link_impl)

///////////////////////////////////////////////////////////////////////////////
//  ilink_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::ilink_impl)
	// serialize inode
	// [NOTE] disabled - inodes aren't actually used now
	// [TODO] reenable later when conception will be more thought out
	//ar(make_nvp("inode", t.inode_));

	serialize<tree::link_impl>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::ilink_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::link_impl, tree::ilink_impl)

///////////////////////////////////////////////////////////////////////////////
//  hard_link_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::hard_link_impl)
	if constexpr(Archive::is_saving::value) {
		ar( make_nvp("data", t.data_) );
	}
	else {
		// load data with deferred 2nd trial
		ar(defer_failed(
			t.data_,
			[&t](auto obj) { t.set_data(std::move(obj)); },
			PtrInitTrigger::SuccessAndRetry
		));
	}

	serialize<tree::ilink_impl>::go(ar, t, version);
	//ar( make_nvp("linkbase", base_class<tree::ilink_impl>(&t)) );
BSS_FCN_INL_END(serialize, tree::hard_link_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::ilink_impl, tree::hard_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::hard_link_impl, "hard_link")

///////////////////////////////////////////////////////////////////////////////
//  weak_link_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::weak_link_impl)
	if constexpr(Archive::is_saving::value) {
		ar( make_nvp("data", t.data_.lock()) );
	}
	else {
		auto unused = sp_obj{};
		ar(defer_failed(
			unused,
			[&t](auto obj) { t.set_data(std::move(obj)); },
			PtrInitTrigger::SuccessAndRetry
		));
	}

	serialize<tree::ilink_impl>::go(ar, t, version);
	//ar( make_nvp("linkbase", base_class<tree::ilink_impl>(&t)) );
BSS_FCN_INL_END(serialize, tree::weak_link_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::ilink_impl, tree::weak_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::weak_link_impl, "weak_link")

///////////////////////////////////////////////////////////////////////////////
//  sym_link_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::sym_link_impl)
	ar(make_nvp("path", t.path_));

	serialize<tree::link_impl>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::sym_link_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::link_impl, tree::sym_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::sym_link_impl, "sym_link")

///////////////////////////////////////////////////////////////////////////////
//  fusion_link_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::fusion_link_impl)
	if constexpr(Archive::is_saving::value) {
		ar( make_nvp("bridge", t.bridge_) );
	}
	else {
		// load bridge with deferred trial
		ar(defer_failed(
			t.bridge_,
			[&t](auto B) { t.bridge_ = std::move(B); },
			PtrInitTrigger::SuccessAndRetry
		));
	}

	serialize<tree::ilink_impl>::go(ar, t, version);
	//ar( make_nvp("linkbase", base_class<tree::ilink_impl>(&t)) );
BSS_FCN_INL_END(serialize, tree::fusion_link_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::ilink_impl, tree::fusion_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::fusion_link_impl, "fusion_link")

///////////////////////////////////////////////////////////////////////////////
//  map_link - not serialized
//
CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::link_impl, tree::map_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::map_link_impl, "map_link")

/*-----------------------------------------------------------------------------
 *  link
 *-----------------------------------------------------------------------------*/
BSS_FCN_BEGIN(serialize, tree::link)
	// map links are not serialized at all
	if(t.type_id() == tree::map_link::type_id_()) return;

	// [NOTE] intentionally do net serialize owner, it will be set up when parent node is loaded
	if constexpr(Archive::is_saving::value) {
		// for nil links save empty impl pointer
		auto Limpl = t ? t.pimpl_ : tree::sp_limpl{};
		ar(make_nvp("link", Limpl));
	}
	else {
		ar(make_nvp("link", t.pimpl_));
		if(!t.pimpl_) t = tree::link{};
		// ID is ready, we can start internal actor
		t.start_engine();
		// if pointee is a node, correct it's handle
		t.pimpl()->propagate_handle();
	}
BSS_FCN_END

BSS_FCN_EXPORT(serialize, tree::link)

BSS_REGISTER_DYNAMIC_INIT(link)
<|endoftext|>


<commit_before>/// @file
/// @author uentity
/// @date 28.06.2018
/// @brief BS tree links serialization code
/// @copyright
/// This Source Code Form is subject to the terms of the Mozilla Public License,
/// v. 2.0. If a copy of the MPL was not distributed with this file,
/// You can obtain one at https://mozilla.org/MPL/2.0/

#include <bs/serialize/serialize.h>
#include <bs/serialize/tree.h>
#include <bs/serialize/boost_uuid.h>

#include "tree_impl.h"
#include "../tree/link_actor.h"
#include "../tree/hard_link.h"
#include "../tree/fusion_link_actor.h"
#include "../tree/map_engine.h"

#include <cereal/types/chrono.hpp>
#include <cereal/types/unordered_map.hpp>

using namespace cereal;
using namespace blue_sky;

/*-----------------------------------------------------------------------------
 *  inode
 *-----------------------------------------------------------------------------*/
BSS_FCN_BEGIN(save, tree::inode)
	ar(
		make_nvp("flags", t.flags),
		make_nvp("u", t.u),
		make_nvp("g", t.g),
		make_nvp("o", t.o),
		make_nvp("mod_time", t.mod_time),
		make_nvp("owner", t.owner),
		make_nvp("group", t.group)
	);
BSS_FCN_END

BSS_FCN_BEGIN(load, tree::inode)
	tree::inode::Flags flags;
	ar(make_nvp("flags", flags));
	t.flags = flags;
	std::uint8_t r;
	ar(make_nvp("u", r));
	t.u = r;
	ar(make_nvp("g", r));
	t.g = r;
	ar(make_nvp("o", r));
	t.o = r;
	ar(
		make_nvp("mod_time", t.mod_time),
		make_nvp("owner", t.owner),
		make_nvp("group", t.group)
	);
BSS_FCN_END

BSS_FCN_EXPORT(save, tree::inode)
BSS_FCN_EXPORT(load, tree::inode)

/*-----------------------------------------------------------------------------
 *  link_impl
 *-----------------------------------------------------------------------------*/
BSS_FCN_BEGIN(serialize, tree::link_impl)
	// [NOTE] intentionally do net serialize owner, it will be set up when parent node is loaded
	ar(
		make_nvp("id", t.id_),
		make_nvp("name", t.name_),
		make_nvp("flags", t.flags_)
	);
BSS_FCN_END

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::engine::impl, tree::link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::link_impl, "link")
BSS_FCN_EXPORT(serialize, tree::link_impl)

///////////////////////////////////////////////////////////////////////////////
//  ilink_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::ilink_impl)
	// serialize inode
	// [NOTE] disabled - inodes aren't actually used now
	// [TODO] reenable later when conception will be more thought out
	//ar(make_nvp("inode", t.inode_));

	serialize<tree::link_impl>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::ilink_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::link_impl, tree::ilink_impl)

///////////////////////////////////////////////////////////////////////////////
//  hard_link_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::hard_link_impl)
	if constexpr(Archive::is_saving::value) {
		ar( make_nvp("data", t.data_) );
	}
	else {
		// load data with deferred 2nd trial
		ar(defer_failed(
			t.data_,
			[&t](auto obj) { t.set_data(std::move(obj)); },
			PtrInitTrigger::SuccessAndRetry
		));
	}

	serialize<tree::ilink_impl>::go(ar, t, version);
	//ar( make_nvp("linkbase", base_class<tree::ilink_impl>(&t)) );
BSS_FCN_INL_END(serialize, tree::hard_link_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::ilink_impl, tree::hard_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::hard_link_impl, "hard_link")

///////////////////////////////////////////////////////////////////////////////
//  weak_link_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::weak_link_impl)
	if constexpr(Archive::is_saving::value) {
		ar( make_nvp("data", t.data_.lock()) );
	}
	else {
		auto unused = sp_obj{};
		ar(defer_failed(
			unused,
			[&t](auto obj) { t.set_data(std::move(obj)); },
			PtrInitTrigger::SuccessAndRetry
		));
	}

	serialize<tree::ilink_impl>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::weak_link_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::ilink_impl, tree::weak_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::weak_link_impl, "weak_link")

///////////////////////////////////////////////////////////////////////////////
//  sym_link_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::sym_link_impl)
	ar(make_nvp("path", t.path_));

	serialize<tree::link_impl>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::sym_link_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::link_impl, tree::sym_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::sym_link_impl, "sym_link")

///////////////////////////////////////////////////////////////////////////////
//  fusion_link_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::fusion_link_impl)
	using namespace blue_sky::tree;
	if constexpr(Archive::is_saving::value) {
		ar( make_nvp("bridge", t.bridge_) );
		// save status values
		ar(
			make_nvp("data_status", t.req_status(Req::Data)),
			make_nvp("dnode_status", t.req_status(Req::DataNode))
		);
		// save cached object
		ar( make_nvp("data", t.data_) );
	}
	else {
		// load bridge with deferred trial
		ar(defer_failed(
			t.bridge_,
			[&t](auto B) { t.bridge_ = std::move(B); },
			PtrInitTrigger::SuccessAndRetry
		));
		// restore status
		tree::ReqStatus s;
		ar(make_nvp("data_status", s));
		t.rs_reset(Req::Data, ReqReset::Always, s);
		ar(make_nvp("dnode_status", s));
		t.rs_reset(Req::DataNode, ReqReset::Always, s);

		// load data with deferred 2nd trial
		ar(defer_failed(
			t.data_,
			[&](auto obj) { t.data_ = std::move(obj); },
			PtrInitTrigger::SuccessAndRetry
		));
	}

	serialize<tree::ilink_impl>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::fusion_link_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::ilink_impl, tree::fusion_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::fusion_link_impl, "fusion_link")

///////////////////////////////////////////////////////////////////////////////
//  map_link
//
BSS_FCN_INL_BEGIN(serialize, tree::map_link_impl_base)
	if constexpr(Archive::is_saving::value) {
		// dump contained node only if map_link owns it
		if(t.out_.handle().id() == t.id_)
			ar(make_nvp( "out", t.out_ ));
		else
			ar(make_nvp( "out", tree::node::nil() ));
	}
	else {
		// load out node
		tree::node out_node;
		ar(make_nvp( "out", out_node ));
		if(t.out_ = out_node)
			t.link_impl::propagate_handle(t.out_);
	}

	serialize<tree::link_impl>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::map_link_impl_base)

BSS_FCN_INL_BEGIN(serialize, tree::map_link_impl)
	ar(make_nvp( "io_map", t.io_map_  ));
	serialize<tree::map_link_impl_base>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::map_link_impl)

BSS_FCN_INL_BEGIN(serialize, tree::map_node_impl)
	serialize<tree::map_link_impl_base>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::map_node_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::link_impl, tree::map_link_impl_base)
CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::map_link_impl_base, tree::map_link_impl)
CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::map_link_impl_base, tree::map_node_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::map_link_impl, "map_link")
CEREAL_REGISTER_TYPE_WITH_NAME(tree::map_node_impl, "map_node")

/*-----------------------------------------------------------------------------
 *  link
 *-----------------------------------------------------------------------------*/
BSS_FCN_BEGIN(serialize, tree::link)
	// [NOTE] intentionally do net serialize owner, it will be set up when parent node is loaded
	if constexpr(Archive::is_saving::value) {
		// for nil link save empty impl pointer
		ar(make_nvp( "link", t ? t.pimpl_ : tree::sp_limpl{} ));
	}
	else {
		ar(make_nvp("link", t.pimpl_));
		if(t.pimpl_) {
			// ID is ready, we can start internal actor
			t.start_engine();
			// if pointee is a node, correct it's handle
			t.pimpl()->propagate_handle();
		}
		else
			t = tree::link{};
	}
BSS_FCN_END

BSS_FCN_EXPORT(serialize, tree::link)

BSS_REGISTER_DYNAMIC_INIT(link)
<commit_msg>serial/tree: refactor status values serialization<commit_after>/// @file
/// @author uentity
/// @date 28.06.2018
/// @brief BS tree links serialization code
/// @copyright
/// This Source Code Form is subject to the terms of the Mozilla Public License,
/// v. 2.0. If a copy of the MPL was not distributed with this file,
/// You can obtain one at https://mozilla.org/MPL/2.0/

#include <bs/serialize/serialize.h>
#include <bs/serialize/tree.h>
#include <bs/serialize/boost_uuid.h>

#include "bs/serialize/carray.h"
#include "tree_impl.h"
#include "../tree/link_actor.h"
#include "../tree/hard_link.h"
#include "../tree/fusion_link_actor.h"
#include "../tree/map_engine.h"

#include <cereal/types/chrono.hpp>
#include <cereal/types/unordered_map.hpp>

using namespace cereal;
using namespace blue_sky;

/*-----------------------------------------------------------------------------
 *  inode
 *-----------------------------------------------------------------------------*/
BSS_FCN_BEGIN(save, tree::inode)
	ar(
		make_nvp("flags", t.flags),
		make_nvp("u", t.u),
		make_nvp("g", t.g),
		make_nvp("o", t.o),
		make_nvp("mod_time", t.mod_time),
		make_nvp("owner", t.owner),
		make_nvp("group", t.group)
	);
BSS_FCN_END

BSS_FCN_BEGIN(load, tree::inode)
	tree::inode::Flags flags;
	ar(make_nvp("flags", flags));
	t.flags = flags;
	std::uint8_t r;
	ar(make_nvp("u", r));
	t.u = r;
	ar(make_nvp("g", r));
	t.g = r;
	ar(make_nvp("o", r));
	t.o = r;
	ar(
		make_nvp("mod_time", t.mod_time),
		make_nvp("owner", t.owner),
		make_nvp("group", t.group)
	);
BSS_FCN_END

BSS_FCN_EXPORT(save, tree::inode)
BSS_FCN_EXPORT(load, tree::inode)

/*-----------------------------------------------------------------------------
 *  link_impl
 *-----------------------------------------------------------------------------*/
BSS_FCN_BEGIN(serialize, tree::link_impl)
	// [NOTE] intentionally do net serialize owner, it will be set up when parent node is loaded
	ar(
		make_nvp("id", t.id_),
		make_nvp("name", t.name_),
		make_nvp("flags", t.flags_)
	);
BSS_FCN_END

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::engine::impl, tree::link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::link_impl, "link")
BSS_FCN_EXPORT(serialize, tree::link_impl)

///////////////////////////////////////////////////////////////////////////////
//  ilink_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::ilink_impl)
	// serialize inode
	// [NOTE] disabled - inodes aren't actually used now
	// [TODO] reenable later when conception will be more thought out
	//ar(make_nvp("inode", t.inode_));

	serialize<tree::link_impl>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::ilink_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::link_impl, tree::ilink_impl)

///////////////////////////////////////////////////////////////////////////////
//  hard_link_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::hard_link_impl)
	if constexpr(Archive::is_saving::value) {
		ar( make_nvp("data", t.data_) );
	}
	else {
		// load data with deferred 2nd trial
		ar(defer_failed(
			t.data_,
			[&t](auto obj) { t.set_data(std::move(obj)); },
			PtrInitTrigger::SuccessAndRetry
		));
	}

	serialize<tree::ilink_impl>::go(ar, t, version);
	//ar( make_nvp("linkbase", base_class<tree::ilink_impl>(&t)) );
BSS_FCN_INL_END(serialize, tree::hard_link_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::ilink_impl, tree::hard_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::hard_link_impl, "hard_link")

///////////////////////////////////////////////////////////////////////////////
//  weak_link_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::weak_link_impl)
	if constexpr(Archive::is_saving::value) {
		ar( make_nvp("data", t.data_.lock()) );
	}
	else {
		auto unused = sp_obj{};
		ar(defer_failed(
			unused,
			[&t](auto obj) { t.set_data(std::move(obj)); },
			PtrInitTrigger::SuccessAndRetry
		));
	}

	serialize<tree::ilink_impl>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::weak_link_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::ilink_impl, tree::weak_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::weak_link_impl, "weak_link")

///////////////////////////////////////////////////////////////////////////////
//  sym_link_impl
//
BSS_FCN_INL_BEGIN(serialize, tree::sym_link_impl)
	ar(make_nvp("path", t.path_));

	serialize<tree::link_impl>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::sym_link_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::link_impl, tree::sym_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::sym_link_impl, "sym_link")

///////////////////////////////////////////////////////////////////////////////
//  fusion_link_impl
//
NAMESPACE_BEGIN()

template<typename Archive>
auto serialize_status(Archive& ar, tree::link_impl& t) {
	using namespace blue_sky::tree;
	using array_t = std::array<tree::ReqStatus, 2>;

	auto stat = [&]() -> array_t {
		if constexpr(Archive::is_saving::value)
			return {t.req_status(Req::Data), t.req_status(Req::DataNode)};
		else return {};
	}();
	serialize_carray(ar, stat, "status");

	if constexpr(Archive::is_loading::value) {
		t.rs_reset(Req::Data, ReqReset::Always, stat[0]);
		t.rs_reset(Req::DataNode, ReqReset::Always, stat[1]);
	}
}

NAMESPACE_END()

BSS_FCN_INL_BEGIN(serialize, tree::fusion_link_impl)
	using namespace blue_sky::tree;

	if constexpr(Archive::is_saving::value) {
		ar( make_nvp("bridge", t.bridge_) );
		// save cached object
		ar( make_nvp("data", t.data_) );
	}
	else {
		// load bridge with deferred trial
		ar(defer_failed(
			t.bridge_,
			[&t](auto B) { t.bridge_ = std::move(B); },
			PtrInitTrigger::SuccessAndRetry
		));
		// load data with deferred 2nd trial
		ar(defer_failed(
			t.data_,
			[&](auto obj) { t.data_ = std::move(obj); },
			PtrInitTrigger::SuccessAndRetry
		));
	}

	serialize_status(ar, t);
	serialize<tree::ilink_impl>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::fusion_link_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::ilink_impl, tree::fusion_link_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::fusion_link_impl, "fusion_link")

///////////////////////////////////////////////////////////////////////////////
//  map_link
//
BSS_FCN_INL_BEGIN(serialize, tree::map_link_impl_base)
	if constexpr(Archive::is_saving::value) {
		// dump contained node only if map_link owns it
		if(t.out_.handle().id() == t.id_)
			ar(make_nvp( "out", t.out_ ));
		else
			ar(make_nvp( "out", tree::node::nil() ));
	}
	else {
		// load out node
		tree::node out_node;
		ar(make_nvp( "out", out_node ));
		if(t.out_ = out_node)
			t.link_impl::propagate_handle(t.out_);
	}

	serialize_status(ar, t);
	serialize<tree::link_impl>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::map_link_impl_base)

BSS_FCN_INL_BEGIN(serialize, tree::map_link_impl)
	ar(make_nvp( "io_map", t.io_map_  ));
	serialize<tree::map_link_impl_base>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::map_link_impl)

BSS_FCN_INL_BEGIN(serialize, tree::map_node_impl)
	serialize<tree::map_link_impl_base>::go(ar, t, version);
BSS_FCN_INL_END(serialize, tree::map_node_impl)

CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::link_impl, tree::map_link_impl_base)
CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::map_link_impl_base, tree::map_link_impl)
CEREAL_REGISTER_POLYMORPHIC_RELATION(tree::map_link_impl_base, tree::map_node_impl)
CEREAL_REGISTER_TYPE_WITH_NAME(tree::map_link_impl, "map_link")
CEREAL_REGISTER_TYPE_WITH_NAME(tree::map_node_impl, "map_node")

/*-----------------------------------------------------------------------------
 *  link
 *-----------------------------------------------------------------------------*/
BSS_FCN_BEGIN(serialize, tree::link)
	// [NOTE] intentionally do net serialize owner, it will be set up when parent node is loaded
	if constexpr(Archive::is_saving::value) {
		// for nil link save empty impl pointer
		ar(make_nvp( "link", t ? t.pimpl_ : tree::sp_limpl{} ));
	}
	else {
		ar(make_nvp("link", t.pimpl_));
		if(t.pimpl_) {
			// ID is ready, we can start internal actor
			t.start_engine();
			// if pointee is a node, correct it's handle
			t.pimpl()->propagate_handle();
		}
		else
			t = tree::link{};
	}
BSS_FCN_END

BSS_FCN_EXPORT(serialize, tree::link)

BSS_REGISTER_DYNAMIC_INIT(link)
<|endoftext|>


<commit_before>//===--- IterativeTypeChecker.cpp - Iterative Type Checker ----------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file implements the IterativeTypeChecker class, which
//  performs iterative type checking by tracking the set of
//  outstanding type-checking requests and servicing them as needed.
//
//===----------------------------------------------------------------------===//

#include "TypeChecker.h"
#include "swift/Sema/IterativeTypeChecker.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/Basic/Defer.h"
using namespace swift;

ASTContext &IterativeTypeChecker::getASTContext() const {
  return TC.Context;
}

DiagnosticEngine &IterativeTypeChecker::getDiags() const {
  return getASTContext().Diags;
}

void IterativeTypeChecker::process(
       TypeCheckRequest request,
       UnsatisfiedDependency unsatisfiedDependency) {
  switch (request.getKind()) {
#define TYPE_CHECK_REQUEST(Request,PayloadName)                   \
  case TypeCheckRequest::Request:                                 \
    return process##Request(request.get##PayloadName##Payload(),  \
                            unsatisfiedDependency);

#include "swift/Sema/TypeCheckRequestKinds.def"
  }
}

/// Determine whether the given request has already been satisfied.
bool IterativeTypeChecker::isSatisfied(TypeCheckRequest request) {
  switch (request.getKind()) {
#define TYPE_CHECK_REQUEST(Request,PayloadName)                         \
  case TypeCheckRequest::Request:                                       \
    return is##Request##Satisfied(request.get##PayloadName##Payload());

#include "swift/Sema/TypeCheckRequestKinds.def"
  }
}

bool IterativeTypeChecker::breakCycle(TypeCheckRequest request) {
  switch (request.getKind()) {
#define TYPE_CHECK_REQUEST(Request,PayloadName)                         \
  case TypeCheckRequest::Request:                                       \
    return breakCycleFor##Request(request.get##PayloadName##Payload());

#include "swift/Sema/TypeCheckRequestKinds.def"
  }
}

void IterativeTypeChecker::satisfy(TypeCheckRequest request) {
  auto startingSize = ActiveRequests.size();

  auto addToActiveRequests = [&](TypeCheckRequest request) {
    // Check for circular dependencies in our requests.
    auto existingRequest = std::find(ActiveRequests.rbegin(),
                                     ActiveRequests.rend(),
                                     request);
    if (existingRequest != ActiveRequests.rend()) {
      auto first = existingRequest.base();
      --first;
      diagnoseCircularReference(llvm::makeArrayRef(&*first,
                                                   &*ActiveRequests.end()));
      return;
    }
    // Add this request to the stack of active requests.
    ActiveRequests.push_back(request);
  };

  addToActiveRequests(request);
  SWIFT_DEFER { ActiveRequests.pop_back(); };

  while (ActiveRequests.size() != startingSize) {
    request = ActiveRequests.back();
    // If the request has already been satisfied, we're done.
    if (isSatisfied(request)) {
      ActiveRequests.pop_back();
      continue;
    }

    // Process this requirement, enumerating dependencies if anything else needs
    // to be handled first.
    process(request, [&](TypeCheckRequest dependency) -> bool {
      if (isSatisfied(dependency)) return false;
      // Record the unsatisfied dependency.
      addToActiveRequests(dependency);
      return true;
    });
  }
}

static bool isSelfRedefinedTypeAliasDecl(const TypeCheckRequest &Request) {
  if (Request.getKind() == TypeCheckRequest::Kind::ResolveTypeDecl) {
    if (auto TAD = dyn_cast<TypeAliasDecl>(Request.getAnchor())) {
      SourceRange SR = TAD->getUnderlyingTypeLoc().getSourceRange();
      SourceManager &SM = TAD->getASTContext().SourceMgr;
      CharSourceRange CR = CharSourceRange(SM, SR.Start,
                                           Lexer::getLocForEndOfToken(SM,
                                                                      SR.End));
      return CR.str() == TAD->getNameStr();
    }
  }
  return false;
}

//===----------------------------------------------------------------------===//
// Diagnostics
//===----------------------------------------------------------------------===//
void IterativeTypeChecker::diagnoseCircularReference(
       ArrayRef<TypeCheckRequest> requests) {
  bool isFirst = true;
  for (const auto &request : requests) {
    if (isSelfRedefinedTypeAliasDecl(request)) {
      diagnose(request.getLoc(), diag::redundant_type_alias_define).
        fixItRemove(request.getAnchor()->getSourceRange());
    } else {
      diagnose(request.getLoc(),
               isFirst ? diag::circular_reference :
                         diag::circular_reference_through);
    }

    isFirst = false;
  }

  // Now try to break the cycle.
#ifndef NDEBUG
  bool brokeCycle = false;
#endif
  for (const auto &request : reverse(requests)) {
    if (breakCycle(request)) {
#ifndef NDEBUG
      brokeCycle = true;
#endif
      break;
    }
  }

  assert(brokeCycle && "Will the cycle be unbroken?");
}
<commit_msg>Revert IterativeTypeChecker changes<commit_after>//===--- IterativeTypeChecker.cpp - Iterative Type Checker ----------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file implements the IterativeTypeChecker class, which
//  performs iterative type checking by tracking the set of
//  outstanding type-checking requests and servicing them as needed.
//
//===----------------------------------------------------------------------===//

#include "TypeChecker.h"
#include "swift/Sema/IterativeTypeChecker.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/Basic/Defer.h"
using namespace swift;

ASTContext &IterativeTypeChecker::getASTContext() const {
  return TC.Context;
}

DiagnosticEngine &IterativeTypeChecker::getDiags() const {
  return getASTContext().Diags;
}

void IterativeTypeChecker::process(
       TypeCheckRequest request,
       UnsatisfiedDependency unsatisfiedDependency) {
  switch (request.getKind()) {
#define TYPE_CHECK_REQUEST(Request,PayloadName)                   \
  case TypeCheckRequest::Request:                                 \
    return process##Request(request.get##PayloadName##Payload(),  \
                            unsatisfiedDependency);

#include "swift/Sema/TypeCheckRequestKinds.def"
  }
}

/// Determine whether the given request has already been satisfied.
bool IterativeTypeChecker::isSatisfied(TypeCheckRequest request) {
  switch (request.getKind()) {
#define TYPE_CHECK_REQUEST(Request,PayloadName)                         \
  case TypeCheckRequest::Request:                                       \
    return is##Request##Satisfied(request.get##PayloadName##Payload());

#include "swift/Sema/TypeCheckRequestKinds.def"
  }
}

bool IterativeTypeChecker::breakCycle(TypeCheckRequest request) {
  switch (request.getKind()) {
#define TYPE_CHECK_REQUEST(Request,PayloadName)                         \
  case TypeCheckRequest::Request:                                       \
    return breakCycleFor##Request(request.get##PayloadName##Payload());

#include "swift/Sema/TypeCheckRequestKinds.def"
  }
}

void IterativeTypeChecker::satisfy(TypeCheckRequest request) {
  // If the request has already been satisfied, we're done.
  if (isSatisfied(request)) return;

  // Check for circular dependencies in our requests.
  // FIXME: This stack operation is painfully inefficient.
  auto existingRequest = std::find(ActiveRequests.rbegin(),
                                   ActiveRequests.rend(),
                                   request);
  if (existingRequest != ActiveRequests.rend()) {
    auto first = existingRequest.base();
    --first;
    diagnoseCircularReference(llvm::makeArrayRef(&*first,
                                                 &*ActiveRequests.end()));
    return;
  }

  // Add this request to the stack of active requests.
  ActiveRequests.push_back(request);
  SWIFT_DEFER { ActiveRequests.pop_back(); };

  while (true) {
    // Process this requirement, enumerating dependencies if anything else needs
    // to be handled first.
    SmallVector<TypeCheckRequest, 4> unsatisfied;
    process(request, [&](TypeCheckRequest dependency) -> bool {
      if (isSatisfied(dependency)) return false;

      // Record the unsatisfied dependency.
      unsatisfied.push_back(dependency);
      return true;
    });

    // If there were no unsatisfied dependencies, we're done.
    if (unsatisfied.empty()) {
      assert(isSatisfied(request));
      break;
    }

    // Recurse to satisfy any unsatisfied dependencies.
    // FIXME: Don't recurse in the iterative type checker, silly!
    for (auto dependency : unsatisfied) {
      satisfy(dependency);
    }
  }
}

static bool isSelfRedefinedTypeAliasDecl(const TypeCheckRequest &Request) {
  if (Request.getKind() == TypeCheckRequest::Kind::ResolveTypeDecl) {
    if (auto TAD = dyn_cast<TypeAliasDecl>(Request.getAnchor())) {
      SourceRange SR = TAD->getUnderlyingTypeLoc().getSourceRange();
      SourceManager &SM = TAD->getASTContext().SourceMgr;
      CharSourceRange CR = CharSourceRange(SM, SR.Start,
                                           Lexer::getLocForEndOfToken(SM,
                                                                      SR.End));
      return CR.str() == TAD->getNameStr();
    }
  }
  return false;
}

//===----------------------------------------------------------------------===//
// Diagnostics
//===----------------------------------------------------------------------===//
void IterativeTypeChecker::diagnoseCircularReference(
       ArrayRef<TypeCheckRequest> requests) {
  bool isFirst = true;
  for (const auto &request : requests) {
    if (isSelfRedefinedTypeAliasDecl(request)) {
      diagnose(request.getLoc(), diag::redundant_type_alias_define).
        fixItRemove(request.getAnchor()->getSourceRange());
    } else {
      diagnose(request.getLoc(),
               isFirst ? diag::circular_reference :
                         diag::circular_reference_through);
    }

    isFirst = false;
  }

  // Now try to break the cycle.
#ifndef NDEBUG
  bool brokeCycle = false;
#endif
  for (const auto &request : reverse(requests)) {
    if (breakCycle(request)) {
#ifndef NDEBUG
      brokeCycle = true;
#endif
      break;
    }
  }

  assert(brokeCycle && "Will the cycle be unbroken?");
}
<|endoftext|>


<commit_before>/*
	This file is part of cpp-ethereum.

	cpp-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	cpp-ethereum is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * @author Marek Kotewicz <marek@ethdev.com>
 * @date 2014
 * Unit tests for the solidity compiler JSON Interface output.
 */

#include "../TestHelper.h"
#include <libsolidity/CompilerStack.h>
#include <json/json.h>
#include <libdevcore/Exceptions.h>

namespace dev
{
namespace solidity
{
namespace test
{

class JSONInterfaceChecker
{
public:
	JSONInterfaceChecker(): m_compilerStack(false) {}

	void checkInterface(std::string const& _code, std::string const& _expectedInterfaceString)
	{
		ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse(_code), "Parsing contract failed");
		std::string generatedInterfaceString = m_compilerStack.getMetadata("", DocumentationType::ABIInterface);
		Json::Value generatedInterface;
		m_reader.parse(generatedInterfaceString, generatedInterface);
		Json::Value expectedInterface;
		m_reader.parse(_expectedInterfaceString, expectedInterface);
		BOOST_CHECK_MESSAGE(expectedInterface == generatedInterface,
							"Expected:\n" << expectedInterface.toStyledString() <<
							"\n but got:\n" << generatedInterface.toStyledString());
	}

private:
	CompilerStack m_compilerStack;
	Json::Reader m_reader;
};

BOOST_FIXTURE_TEST_SUITE(SolidityABIJSON, JSONInterfaceChecker)

BOOST_AUTO_TEST_CASE(basic_test)
{
	char const* sourceCode = "contract test {\n"
	"  function f(uint a) returns(uint d) { return a * 7; }\n"
	"}\n";

	char const* interface = R"([
	{
		"name": "f",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "a",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "d",
			"type": "uint256"
		}
		]
	}
	])";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(empty_contract)
{
	char const* sourceCode = "contract test {\n"
	"}\n";
	char const* interface = "[]";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(multiple_methods)
{
	char const* sourceCode = "contract test {\n"
	"  function f(uint a) returns(uint d) { return a * 7; }\n"
	"  function g(uint b) returns(uint e) { return b * 8; }\n"
	"}\n";

	char const* interface = R"([
	{
		"name": "f",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "a",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "d",
			"type": "uint256"
		}
		]
	},
	{
		"name": "g",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "b",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "e",
			"type": "uint256"
		}
		]
	}
	])";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(multiple_params)
{
	char const* sourceCode = "contract test {\n"
	"  function f(uint a, uint b) returns(uint d) { return a + b; }\n"
	"}\n";

	char const* interface = R"([
	{
		"name": "f",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "a",
			"type": "uint256"
		},
		{
			"name": "b",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "d",
			"type": "uint256"
		}
		]
	}
	])";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(multiple_methods_order)
{
	// methods are expected to be in alpabetical order
	char const* sourceCode = "contract test {\n"
	"  function f(uint a) returns(uint d) { return a * 7; }\n"
	"  function c(uint b) returns(uint e) { return b * 8; }\n"
	"}\n";

	char const* interface = R"([
	{
		"name": "c",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "b",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "e",
			"type": "uint256"
		}
		]
	},
	{
		"name": "f",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "a",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "d",
			"type": "uint256"
		}
		]
	}
	])";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(const_function)
{
	char const* sourceCode = "contract test {\n"
	"  function foo(uint a, uint b) returns(uint d) { return a + b; }\n"
	"  function boo(uint32 a) constant returns(uint b) { return a * 4; }\n"
	"}\n";

	char const* interface = R"([
	{
		"name": "foo",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "a",
			"type": "uint256"
		},
		{
			"name": "b",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "d",
			"type": "uint256"
		}
		]
	},
	{
		"name": "boo",
		"constant": true,
		"type": "function",
		"inputs": [{
			"name": "a",
			"type": "uint32"
		}],
		"outputs": [
		{
			"name": "b",
			"type": "uint256"
		}
		]
	}
	])";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(exclude_fallback_function)
{
	char const* sourceCode = "contract test { function() {} }";

	char const* interface = "[]";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(events)
{
	char const* sourceCode = "contract test {\n"
	"  function f(uint a) returns(uint d) { return a * 7; }\n"
	"  event e1(uint b, address indexed c); \n"
	"  event e2(); \n"
	"}\n";
	char const* interface = R"([
	{
		"name": "f",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "a",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "d",
			"type": "uint256"
		}
		]
	},
	{
		"name": "e1",
		"type": "event",
		"anonymous": false,
		"inputs": [
		{
			"indexed": false,
			"name": "b",
			"type": "uint256"
		},
		{
			"indexed": true,
			"name": "c",
			"type": "address"
		}
		]
	},
	{
		"name": "e2",
		"type": "event",
		"anonymous": false,
		"inputs": []
	}

	])";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(events_anonymous)
{
	char const* sourceCode = "contract test {\n"
	"  event e() anonymous; \n"
	"}\n";
	char const* interface = R"([
	{
		"name": "e",
		"type": "event",
		"anonymous": true,
		"inputs": []
	}

	])";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(inherited)
{
	char const* sourceCode =
	"	contract Base { \n"
	"		function baseFunction(uint p) returns (uint i) { return p; } \n"
	"		event baseEvent(bytes32 indexed evtArgBase); \n"
	"	} \n"
	"	contract Derived is Base { \n"
	"		function derivedFunction(bytes32 p) returns (bytes32 i) { return p; } \n"
	"		event derivedEvent(uint indexed evtArgDerived); \n"
	"	}";

	char const* interface = R"([
	{
		"name": "baseFunction",
		"constant": false,
		"type": "function",
		"inputs":
		[{
			"name": "p",
			"type": "uint256"
		}],
		"outputs":
		[{
			"name": "i",
			"type": "uint256"
		}]
	},
	{
		"name": "derivedFunction",
		"constant": false,
		"type": "function",
		"inputs":
		[{
			"name": "p",
			"type": "bytes32"
		}],
		"outputs":
		[{
			"name": "i",
			"type": "bytes32"
		}]
	},
	{
		"name": "derivedEvent",
		"type": "event",
		"anonymous": false,
		"inputs":
		[{
			"indexed": true,
			"name": "evtArgDerived",
			"type": "uint256"
		}]
	},
	{
		"name": "baseEvent",
		"type": "event",
		"anonymous": false,
		"inputs":
		[{
			"indexed": true,
			"name": "evtArgBase",
			"type": "bytes32"
		}]
	}])";

	checkInterface(sourceCode, interface);
}
BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one)
{
	char const* sourceCode = R"(
	contract test {
		function f(uint, uint k) returns(uint ret_k, uint ret_g){
			uint g = 8;
			ret_k = k;
			ret_g = g;
		}
	})";

	char const* interface = R"([
	{
		"name": "f",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "",
			"type": "uint256"
		},
		{
			"name": "k",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "ret_k",
			"type": "uint256"
		},
		{
			"name": "ret_g",
			"type": "uint256"
		}
		]
	}
	])";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(empty_name_return_parameter)
{
	char const* sourceCode = R"(
		contract test {
		function f(uint k) returns(uint){
			return k;
		}
	})";

	char const* interface = R"([
	{
		"name": "f",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "k",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "",
			"type": "uint256"
		}
		]
	}
	])";
	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_SUITE_END()

}
}
}
<commit_msg>test for resalts<commit_after>/*
	This file is part of cpp-ethereum.

	cpp-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	cpp-ethereum is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * @author Marek Kotewicz <marek@ethdev.com>
 * @date 2014
 * Unit tests for the solidity compiler JSON Interface output.
 */

#include "../TestHelper.h"
#include <libsolidity/CompilerStack.h>
#include <json/json.h>
#include <libdevcore/Exceptions.h>

namespace dev
{
namespace solidity
{
namespace test
{

class JSONInterfaceChecker
{
public:
	JSONInterfaceChecker(): m_compilerStack(false) {}

	void checkInterface(std::string const& _code, std::string const& _expectedInterfaceString)
	{
		ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse(_code), "Parsing contract failed");
		std::string generatedInterfaceString = m_compilerStack.getMetadata("", DocumentationType::ABIInterface);
		Json::Value generatedInterface;
		m_reader.parse(generatedInterfaceString, generatedInterface);
		Json::Value expectedInterface;
		m_reader.parse(_expectedInterfaceString, expectedInterface);
		BOOST_CHECK_MESSAGE(expectedInterface == generatedInterface,
							"Expected:\n" << expectedInterface.toStyledString() <<
							"\n but got:\n" << generatedInterface.toStyledString());
	}

private:
	CompilerStack m_compilerStack;
	Json::Reader m_reader;
};

BOOST_FIXTURE_TEST_SUITE(SolidityABIJSON, JSONInterfaceChecker)

BOOST_AUTO_TEST_CASE(basic_test)
{
	char const* sourceCode = "contract test {\n"
	"  function f(uint a) returns(uint d) { return a * 7; }\n"
	"}\n";

	char const* interface = R"([
	{
		"name": "f",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "a",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "d",
			"type": "uint256"
		}
		]
	}
	])";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(empty_contract)
{
	char const* sourceCode = "contract test {\n"
	"}\n";
	char const* interface = "[]";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(multiple_methods)
{
	char const* sourceCode = "contract test {\n"
	"  function f(uint a) returns(uint d) { return a * 7; }\n"
	"  function g(uint b) returns(uint e) { return b * 8; }\n"
	"}\n";

	char const* interface = R"([
	{
		"name": "f",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "a",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "d",
			"type": "uint256"
		}
		]
	},
	{
		"name": "g",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "b",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "e",
			"type": "uint256"
		}
		]
	}
	])";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(multiple_params)
{
	char const* sourceCode = "contract test {\n"
	"  function f(uint a, uint b) returns(uint d) { return a + b; }\n"
	"}\n";

	char const* interface = R"([
	{
		"name": "f",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "a",
			"type": "uint256"
		},
		{
			"name": "b",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "d",
			"type": "uint256"
		}
		]
	}
	])";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(multiple_methods_order)
{
	// methods are expected to be in alpabetical order
	char const* sourceCode = "contract test {\n"
	"  function f(uint a) returns(uint d) { return a * 7; }\n"
	"  function c(uint b) returns(uint e) { return b * 8; }\n"
	"}\n";

	char const* interface = R"([
	{
		"name": "c",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "b",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "e",
			"type": "uint256"
		}
		]
	},
	{
		"name": "f",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "a",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "d",
			"type": "uint256"
		}
		]
	}
	])";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(const_function)
{
	char const* sourceCode = "contract test {\n"
	"  function foo(uint a, uint b) returns(uint d) { return a + b; }\n"
	"  function boo(uint32 a) constant returns(uint b) { return a * 4; }\n"
	"}\n";

	char const* interface = R"([
	{
		"name": "foo",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "a",
			"type": "uint256"
		},
		{
			"name": "b",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "d",
			"type": "uint256"
		}
		]
	},
	{
		"name": "boo",
		"constant": true,
		"type": "function",
		"inputs": [{
			"name": "a",
			"type": "uint32"
		}],
		"outputs": [
		{
			"name": "b",
			"type": "uint256"
		}
		]
	}
	])";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(exclude_fallback_function)
{
	char const* sourceCode = "contract test { function() {} }";

	char const* interface = "[]";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(events)
{
	char const* sourceCode = "contract test {\n"
	"  function f(uint a) returns(uint d) { return a * 7; }\n"
	"  event e1(uint b, address indexed c); \n"
	"  event e2(); \n"
	"}\n";
	char const* interface = R"([
	{
		"name": "f",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "a",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "d",
			"type": "uint256"
		}
		]
	},
	{
		"name": "e1",
		"type": "event",
		"anonymous": false,
		"inputs": [
		{
			"indexed": false,
			"name": "b",
			"type": "uint256"
		},
		{
			"indexed": true,
			"name": "c",
			"type": "address"
		}
		]
	},
	{
		"name": "e2",
		"type": "event",
		"anonymous": false,
		"inputs": []
	}

	])";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(events_anonymous)
{
	char const* sourceCode = "contract test {\n"
	"  event e() anonymous; \n"
	"}\n";
	char const* interface = R"([
	{
		"name": "e",
		"type": "event",
		"anonymous": true,
		"inputs": []
	}

	])";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(inherited)
{
	char const* sourceCode =
	"	contract Base { \n"
	"		function baseFunction(uint p) returns (uint i) { return p; } \n"
	"		event baseEvent(bytes32 indexed evtArgBase); \n"
	"	} \n"
	"	contract Derived is Base { \n"
	"		function derivedFunction(bytes32 p) returns (bytes32 i) { return p; } \n"
	"		event derivedEvent(uint indexed evtArgDerived); \n"
	"	}";

	char const* interface = R"([
	{
		"name": "baseFunction",
		"constant": false,
		"type": "function",
		"inputs":
		[{
			"name": "p",
			"type": "uint256"
		}],
		"outputs":
		[{
			"name": "i",
			"type": "uint256"
		}]
	},
	{
		"name": "derivedFunction",
		"constant": false,
		"type": "function",
		"inputs":
		[{
			"name": "p",
			"type": "bytes32"
		}],
		"outputs":
		[{
			"name": "i",
			"type": "bytes32"
		}]
	},
	{
		"name": "derivedEvent",
		"type": "event",
		"anonymous": false,
		"inputs":
		[{
			"indexed": true,
			"name": "evtArgDerived",
			"type": "uint256"
		}]
	},
	{
		"name": "baseEvent",
		"type": "event",
		"anonymous": false,
		"inputs":
		[{
			"indexed": true,
			"name": "evtArgBase",
			"type": "bytes32"
		}]
	}])";

	checkInterface(sourceCode, interface);
}
BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one)
{
	char const* sourceCode = R"(
	contract test {
		function f(uint, uint k) returns(uint ret_k, uint ret_g){
			uint g = 8;
			ret_k = k;
			ret_g = g;
		}
	})";

	char const* interface = R"([
	{
		"name": "f",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "",
			"type": "uint256"
		},
		{
			"name": "k",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "ret_k",
			"type": "uint256"
		},
		{
			"name": "ret_g",
			"type": "uint256"
		}
		]
	}
	])";

	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(empty_name_return_parameter)
{
	char const* sourceCode = R"(
		contract test {
		function f(uint k) returns(uint){
			return k;
		}
	})";

	char const* interface = R"([
	{
		"name": "f",
		"constant": false,
		"type": "function",
		"inputs": [
		{
			"name": "k",
			"type": "uint256"
		}
		],
		"outputs": [
		{
			"name": "",
			"type": "uint256"
		}
		]
	}
	])";
	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_CASE(constructor_abi)
{
	char const* sourceCode = R"(
		contract test {
		function test() {
		}
	})";

	char const* interface = R"("
	[
		{
		  "constant" : false,
		  "inputs" : [],
		  "name" : "test",
		  "outputs" : [],
		  "type" : "constructor"
		}
	])";
	checkInterface(sourceCode, interface);
}

BOOST_AUTO_TEST_SUITE_END()

}
}
}
<|endoftext|>


<commit_before>/*
*  specguiorder.cpp
*  StatusSpec project
*
*  Copyright (c) 2014 thesupremecommander
*  BSD 2-Clause License
*  http://opensource.org/licenses/BSD-2-Clause
*
*/

#include "specguiorder.h"

SpecGUIOrder::SpecGUIOrder() {
	enabled = new ConVar("statusspec_specguiorder_enabled", "0", FCVAR_NONE, "enable ordering of spec GUI");
	reverse_blu = new ConVar("statusspec_specguiorder_reverse_blu", "0", FCVAR_NONE, "reverse order for BLU players");
	reverse_red = new ConVar("statusspec_specguiorder_reverse_red", "0", FCVAR_NONE, "reverse order for RED players");

	specguiSettings = new KeyValues("Resource/UI/SpectatorTournament.res");
	specguiSettings->LoadFromFile(Interfaces::pFileSystem, "resource/ui/spectatortournament.res", "mod");
}

bool SpecGUIOrder::IsEnabled() {
	return enabled->GetBool();
}

void SpecGUIOrder::InterceptMessage(vgui::VPANEL vguiPanel, KeyValues *params, vgui::VPANEL ifromPanel) {
	std::string originPanelName = g_pVGuiPanel->GetName(ifromPanel);

	if (originPanelName.substr(0, 11).compare("playerpanel") == 0 && strcmp(params->GetName(), "DialogVariables") == 0) {
		const char *playerName = params->GetString("playername", NULL);

		if (playerName) {
			for (int i = 0; i <= MAX_PLAYERS; i++) {
				Player player = i;

				if (!player) {
					continue;
				}

				if (strcmp(playerName, player.GetName()) == 0) {
					playerPanels[originPanelName] = player;

					break;
				}
			}
		}
	}
}

bool SpecGUIOrder::SetPosOverride(vgui::VPANEL vguiPanel, int &x, int &y) {
	std::string panelName = g_pVGuiPanel->GetName(vguiPanel);

	if (panelName.substr(0, 11).compare("playerpanel") == 0) {
		Player player = playerPanels[panelName];

		if (!player) {
			return false;
		}

		TFTeam team = player.GetTeam();

		if (team == TFTeam_Red) {
			int position;

			if (!reverse_red->GetBool()) {
				position = std::distance(redPlayers.begin(), std::find(redPlayers.begin(), redPlayers.end(), player));
			}
			else {
				position = std::distance(redPlayers.rbegin(), std::find(redPlayers.rbegin(), redPlayers.rend(), player));
			}

			int baseX = specguiSettings->FindKey("specgui")->GetInt("team2_player_base_offset_x");
			int baseY = specguiSettings->FindKey("specgui")->GetInt("team2_player_base_y");
			int deltaX = specguiSettings->FindKey("specgui")->GetInt("team2_player_delta_x");
			int deltaY = specguiSettings->FindKey("specgui")->GetInt("team2_player_delta_y");

			x = g_pVGuiSchemeManager->GetProportionalScaledValue(baseX + (position * deltaX));
			y = g_pVGuiSchemeManager->GetProportionalScaledValue(baseY + (position * deltaY));

			return true;
		}
		else if (team == TFTeam_Blue) {
			int position;

			if (!reverse_red->GetBool()) {
				position = std::distance(bluPlayers.begin(), std::find(bluPlayers.begin(), bluPlayers.end(), player));
			}
			else {
				position = std::distance(bluPlayers.rbegin(), std::find(bluPlayers.rbegin(), bluPlayers.rend(), player));
			}

			int baseX = specguiSettings->FindKey("specgui")->GetInt("team1_player_base_offset_x");
			int baseY = specguiSettings->FindKey("specgui")->GetInt("team1_player_base_y");
			int deltaX = specguiSettings->FindKey("specgui")->GetInt("team1_player_delta_x");
			int deltaY = specguiSettings->FindKey("specgui")->GetInt("team1_player_delta_y");

			x = g_pVGuiSchemeManager->GetProportionalScaledValue(baseX + (position * deltaX));
			y = g_pVGuiSchemeManager->GetProportionalScaledValue(baseY + (position * deltaY));

			return true;
		}
	}

	return false;
}

void SpecGUIOrder::PreEntityUpdate() {
	bluPlayers.clear();
	redPlayers.clear();
}

void SpecGUIOrder::ProcessEntity(IClientEntity *entity) {
	Player player = entity;

	if (!player) {
		return;
	}

	TFTeam team = player.GetTeam();

	if (team == TFTeam_Red) {
		redPlayers.push_back(player);
	}
	else if (team == TFTeam_Blue) {
		bluPlayers.push_back(player);
	}
}

void SpecGUIOrder::PostEntityUpdate() {
	bluPlayers.sort();
	redPlayers.sort();
}<commit_msg>Fix wrong variable for BLU players.<commit_after>/*
*  specguiorder.cpp
*  StatusSpec project
*
*  Copyright (c) 2014 thesupremecommander
*  BSD 2-Clause License
*  http://opensource.org/licenses/BSD-2-Clause
*
*/

#include "specguiorder.h"

SpecGUIOrder::SpecGUIOrder() {
	enabled = new ConVar("statusspec_specguiorder_enabled", "0", FCVAR_NONE, "enable ordering of spec GUI");
	reverse_blu = new ConVar("statusspec_specguiorder_reverse_blu", "0", FCVAR_NONE, "reverse order for BLU players");
	reverse_red = new ConVar("statusspec_specguiorder_reverse_red", "0", FCVAR_NONE, "reverse order for RED players");

	specguiSettings = new KeyValues("Resource/UI/SpectatorTournament.res");
	specguiSettings->LoadFromFile(Interfaces::pFileSystem, "resource/ui/spectatortournament.res", "mod");
}

bool SpecGUIOrder::IsEnabled() {
	return enabled->GetBool();
}

void SpecGUIOrder::InterceptMessage(vgui::VPANEL vguiPanel, KeyValues *params, vgui::VPANEL ifromPanel) {
	std::string originPanelName = g_pVGuiPanel->GetName(ifromPanel);

	if (originPanelName.substr(0, 11).compare("playerpanel") == 0 && strcmp(params->GetName(), "DialogVariables") == 0) {
		const char *playerName = params->GetString("playername", NULL);

		if (playerName) {
			for (int i = 0; i <= MAX_PLAYERS; i++) {
				Player player = i;

				if (!player) {
					continue;
				}

				if (strcmp(playerName, player.GetName()) == 0) {
					playerPanels[originPanelName] = player;

					break;
				}
			}
		}
	}
}

bool SpecGUIOrder::SetPosOverride(vgui::VPANEL vguiPanel, int &x, int &y) {
	std::string panelName = g_pVGuiPanel->GetName(vguiPanel);

	if (panelName.substr(0, 11).compare("playerpanel") == 0) {
		Player player = playerPanels[panelName];

		if (!player) {
			return false;
		}

		TFTeam team = player.GetTeam();

		if (team == TFTeam_Red) {
			int position;

			if (!reverse_red->GetBool()) {
				position = std::distance(redPlayers.begin(), std::find(redPlayers.begin(), redPlayers.end(), player));
			}
			else {
				position = std::distance(redPlayers.rbegin(), std::find(redPlayers.rbegin(), redPlayers.rend(), player));
			}

			int baseX = specguiSettings->FindKey("specgui")->GetInt("team2_player_base_offset_x");
			int baseY = specguiSettings->FindKey("specgui")->GetInt("team2_player_base_y");
			int deltaX = specguiSettings->FindKey("specgui")->GetInt("team2_player_delta_x");
			int deltaY = specguiSettings->FindKey("specgui")->GetInt("team2_player_delta_y");

			x = g_pVGuiSchemeManager->GetProportionalScaledValue(baseX + (position * deltaX));
			y = g_pVGuiSchemeManager->GetProportionalScaledValue(baseY + (position * deltaY));

			return true;
		}
		else if (team == TFTeam_Blue) {
			int position;

			if (!reverse_blu->GetBool()) {
				position = std::distance(bluPlayers.begin(), std::find(bluPlayers.begin(), bluPlayers.end(), player));
			}
			else {
				position = std::distance(bluPlayers.rbegin(), std::find(bluPlayers.rbegin(), bluPlayers.rend(), player));
			}

			int baseX = specguiSettings->FindKey("specgui")->GetInt("team1_player_base_offset_x");
			int baseY = specguiSettings->FindKey("specgui")->GetInt("team1_player_base_y");
			int deltaX = specguiSettings->FindKey("specgui")->GetInt("team1_player_delta_x");
			int deltaY = specguiSettings->FindKey("specgui")->GetInt("team1_player_delta_y");

			x = g_pVGuiSchemeManager->GetProportionalScaledValue(baseX + (position * deltaX));
			y = g_pVGuiSchemeManager->GetProportionalScaledValue(baseY + (position * deltaY));

			return true;
		}
	}

	return false;
}

void SpecGUIOrder::PreEntityUpdate() {
	bluPlayers.clear();
	redPlayers.clear();
}

void SpecGUIOrder::ProcessEntity(IClientEntity *entity) {
	Player player = entity;

	if (!player) {
		return;
	}

	TFTeam team = player.GetTeam();

	if (team == TFTeam_Red) {
		redPlayers.push_back(player);
	}
	else if (team == TFTeam_Blue) {
		bluPlayers.push_back(player);
	}
}

void SpecGUIOrder::PostEntityUpdate() {
	bluPlayers.sort();
	redPlayers.sort();
}<|endoftext|>


<commit_before>#include "testshelpers.h"
#include <QThread>
#include <QCoreApplication>

void sleepWait(int seconds, const std::function<bool ()> &condition) {
    int times = 0;
    while (times < seconds) {
        if (!condition()) {
            QCoreApplication::processEvents(QEventLoop::AllEvents);
            QThread::sleep(1);
        }

        times++;
    }
}
<commit_msg>Fix for sleepwait helper<commit_after>#include "testshelpers.h"
#include <QThread>
#include <QCoreApplication>
#include <QDebug>

void sleepWait(int seconds, const std::function<bool ()> &condition) {
    int times = 0;
    bool becameTrue = false;

    while (times < seconds) {
        if (!condition()) {
            QCoreApplication::processEvents(QEventLoop::AllEvents);
            QThread::sleep(1);
            times++;
        } else {
            qDebug() << "Condition became true in" << times << "try out of" << seconds;
            becameTrue = true;
            break;
        }
    }

    if (!becameTrue) {
        qDebug() << "Condition never was true";
    }
}
<|endoftext|>


<commit_before>#include "ResultDialog.h"
#include "ui_ResultDialog.h"
#include "StAssert.h"
#include "LanguageHelper.h"

#include <QDesktopWidget>
#include <QMouseEvent>
#include <QMenu>

ResultDialog::ResultDialog (const LanguageHelper &dictionary, QWidget *parent) :
  QDialog (parent),
  ui (new Ui::ResultDialog),
  dictionary_ (dictionary), isShowAtCapturePos_ (true),
  contextMenu_ (NULL), recognizeSubMenu_ (NULL), clipboardAction_ (NULL) {
  ui->setupUi (this);
  setWindowFlags (Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint |
                  Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint);

  installEventFilter (this);
  createContextMenu ();
  applySettings ();
}

ResultDialog::~ResultDialog () {
  delete contextMenu_;
  delete ui;
}

const ProcessingItem &ResultDialog::item () const {
  return item_;
}

void ResultDialog::applySettings () {
  dictionary_.updateMenu (recognizeSubMenu_, dictionary_.availableOcrLanguagesUi ());
}

void ResultDialog::createContextMenu () {
  contextMenu_ = new QMenu ();
  recognizeSubMenu_ = contextMenu_->addMenu (tr ("Распознать другой язык"));
  clipboardAction_ = contextMenu_->addAction (tr ("Скопировать в буфер"));
}

bool ResultDialog::eventFilter (QObject *object, QEvent *event) {
  Q_UNUSED (object);
  if (event->type () == QEvent::MouseButtonPress) {
    Qt::MouseButton button = static_cast<QMouseEvent *>(event)->button ();
    if (button == Qt::RightButton) {
      QAction *action = contextMenu_->exec (QCursor::pos ());
      QWidget *subMenu = action->parentWidget ();
      if (recognizeSubMenu_->isAncestorOf (subMenu)) {
        ProcessingItem item = item_;
        item.translated = item.recognized = QString ();
        item.ocrLanguage = dictionary_.ocrUiToCode (action->text ());
        emit requestRecognize (item);
      }
      else if (action == clipboardAction_) {
        emit requestClipboard ();
      }
    }
    hide ();
  }
  else if (event->type () == QEvent::WindowDeactivate) {
    hide ();
  }
  return QDialog::eventFilter (object, event);
}

void ResultDialog::showResult (ProcessingItem item) {
  ST_ASSERT (item.isValid ());
  item_ = item;
  ui->sourceLabel->setPixmap (item.source);
  ui->recognizeLabel->setText (item.recognized);
  ui->translateLabel->setText (item.translated);
  bool gotTranslation = !item.translated.isEmpty ();
  ui->translateLabel->setVisible (gotTranslation);
  ui->translateLine->setVisible (gotTranslation);

  show ();
  adjustSize ();

  QDesktopWidget *desktop = QApplication::desktop ();
  Q_CHECK_PTR (desktop);
  if (isShowAtCapturePos_) {
    QPoint correction = QPoint (ui->frame->lineWidth (), ui->frame->lineWidth ());
    move (item.screenPos - correction);
    QRect screenRect = desktop->screenGeometry (this);
    int minY = screenRect.bottom () - height ();
    if (y () > minY) {
      move (x (), minY);
    }
  }
  else {

    QRect screenRect = desktop->availableGeometry (this);
    ST_ASSERT (screenRect.isValid ());
    QPoint newPos (screenRect.width () - width (), screenRect.height () - height ());
    move (newPos);
  }
  activateWindow ();
}
<commit_msg>More correct submenu detection.<commit_after>#include "ResultDialog.h"
#include "ui_ResultDialog.h"
#include "StAssert.h"
#include "LanguageHelper.h"

#include <QDesktopWidget>
#include <QMouseEvent>
#include <QMenu>

ResultDialog::ResultDialog (const LanguageHelper &dictionary, QWidget *parent) :
  QDialog (parent),
  ui (new Ui::ResultDialog),
  dictionary_ (dictionary), isShowAtCapturePos_ (true),
  contextMenu_ (NULL), recognizeSubMenu_ (NULL), clipboardAction_ (NULL) {
  ui->setupUi (this);
  setWindowFlags (Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint |
                  Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint);

  installEventFilter (this);
  createContextMenu ();
  applySettings ();
}

ResultDialog::~ResultDialog () {
  delete contextMenu_;
  delete ui;
}

const ProcessingItem &ResultDialog::item () const {
  return item_;
}

void ResultDialog::applySettings () {
  dictionary_.updateMenu (recognizeSubMenu_, dictionary_.availableOcrLanguagesUi ());
}

void ResultDialog::createContextMenu () {
  contextMenu_ = new QMenu ();
  recognizeSubMenu_ = contextMenu_->addMenu (tr ("Распознать другой язык"));
  clipboardAction_ = contextMenu_->addAction (tr ("Скопировать в буфер"));
}

bool ResultDialog::eventFilter (QObject *object, QEvent *event) {
  Q_UNUSED (object);
  if (event->type () == QEvent::MouseButtonPress) {
    Qt::MouseButton button = static_cast<QMouseEvent *>(event)->button ();
    if (button == Qt::RightButton) {
      QAction *action = contextMenu_->exec (QCursor::pos ());
      if (recognizeSubMenu_->findChildren<QAction *> ().contains (action)) {
        ProcessingItem item = item_;
        item.translated = item.recognized = QString ();
        item.ocrLanguage = dictionary_.ocrUiToCode (action->text ());
        emit requestRecognize (item);
      }
      else if (action == clipboardAction_) {
        emit requestClipboard ();
      }
    }
    hide ();
  }
  else if (event->type () == QEvent::WindowDeactivate) {
    hide ();
  }
  return QDialog::eventFilter (object, event);
}

void ResultDialog::showResult (ProcessingItem item) {
  ST_ASSERT (item.isValid ());
  item_ = item;
  ui->sourceLabel->setPixmap (item.source);
  ui->recognizeLabel->setText (item.recognized);
  ui->translateLabel->setText (item.translated);
  bool gotTranslation = !item.translated.isEmpty ();
  ui->translateLabel->setVisible (gotTranslation);
  ui->translateLine->setVisible (gotTranslation);

  show ();
  adjustSize ();

  QDesktopWidget *desktop = QApplication::desktop ();
  Q_CHECK_PTR (desktop);
  if (isShowAtCapturePos_) {
    QPoint correction = QPoint (ui->frame->lineWidth (), ui->frame->lineWidth ());
    move (item.screenPos - correction);
    QRect screenRect = desktop->screenGeometry (this);
    int minY = screenRect.bottom () - height ();
    if (y () > minY) {
      move (x (), minY);
    }
  }
  else {

    QRect screenRect = desktop->availableGeometry (this);
    ST_ASSERT (screenRect.isValid ());
    QPoint newPos (screenRect.width () - width (), screenRect.height () - height ());
    move (newPos);
  }
  activateWindow ();
}
<|endoftext|>