<commit_before>// Scintilla source code edit control
/** @file CallTip.cxx
 ** Code for displaying call tips.
 **/
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

#include <stdlib.h>
#include <string.h>

#include "Platform.h"

#include "Scintilla.h"
#include "CallTip.h"

CallTip::CallTip() {
	wCallTip = 0;
	inCallTipMode = false;
	posStartCallTip = 0;
	val = 0;
	xUp = -100;
	xDown = -100;
	lineHeight = 1;
	startHighlight = 0;
	endHighlight = 0;

	colourBG.desired = ColourDesired(0xff, 0xff, 0xff);
	colourUnSel.desired = ColourDesired(0x80, 0x80, 0x80);
	colourSel.desired = ColourDesired(0, 0, 0x80);
	colourShade.desired = ColourDesired(0, 0, 0);
	colourLight.desired = ColourDesired(0xc0, 0xc0, 0xc0);
}

CallTip::~CallTip() {
	font.Release();
	wCallTip.Destroy();
	delete []val;
	val = 0;
}

const int widthArrow = 14;

void CallTip::RefreshColourPalette(Palette &pal, bool want) {
	pal.WantFind(colourBG, want);
	pal.WantFind(colourUnSel, want);
	pal.WantFind(colourSel, want);
	pal.WantFind(colourShade, want);
	pal.WantFind(colourLight, want);
}

static bool IsArrowCharacter(char ch) {
	return (ch == 0) || (ch == '\001') || (ch == '\002');
}

void CallTip::DrawChunk(Surface *surface, int &x, const char *s,
	int posStart, int posEnd, int ytext, PRectangle rcClient,
	bool highlight, bool draw) {
	s += posStart;
	int len = posEnd - posStart;
	int maxEnd = 0;
	int ends[10];
	for (int i=0;i<len;i++) {
		if (IsArrowCharacter(s[i])) {
			if (i > 0)
				ends[maxEnd++] = i;
			ends[maxEnd++] = i+1;
		}
	}
	ends[maxEnd++] = len;
	int startSeg = 0;
	int xEnd;
	for (int seg = 0; seg<maxEnd; seg++) {
		int endSeg = ends[seg];
		if (endSeg > startSeg) {
			if (IsArrowCharacter(s[startSeg])) {
				xEnd = x + widthArrow;
				offsetMain = xEnd;
				if (draw) {
					const int halfWidth = widthArrow / 2 - 3;
					const int centreX = x + widthArrow / 2 - 1;
					const int centreY = (rcClient.top + rcClient.bottom) / 2;
					rcClient.left = x;
					rcClient.right = xEnd;
					surface->FillRectangle(rcClient, colourBG.allocated);
					PRectangle rcClientInner(rcClient.left+1, rcClient.top+1, rcClient.right-2, rcClient.bottom-1);
					surface->FillRectangle(rcClientInner, colourUnSel.allocated);

					if (s[startSeg] == '\001') {
						// Up arrow
						Point pts[] = {
    						Point(centreX - halfWidth, centreY + halfWidth / 2),
    						Point(centreX + halfWidth, centreY + halfWidth / 2),
    						Point(centreX, centreY - halfWidth + halfWidth / 2),
						};
						surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
                 						colourBG.allocated, colourBG.allocated);
					} else {
						// Down arrow
						Point pts[] = {
    						Point(centreX - halfWidth, centreY - halfWidth / 2),
    						Point(centreX + halfWidth, centreY - halfWidth / 2),
    						Point(centreX, centreY + halfWidth - halfWidth / 2),
						};
						surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
                 						colourBG.allocated, colourBG.allocated);
					}
				} else {
					if (s[startSeg] == '\001') {
						xUp = x+1;
					} else {
						xDown = x+1;
					}
				}
			} else {
				xEnd = x + surface->WidthText(font, s+startSeg, endSeg - startSeg);
				if (draw) {
					rcClient.left = x;
					rcClient.right = xEnd;
					surface->DrawTextNoClip(rcClient, font, ytext,
										s+startSeg, endSeg - startSeg,
										highlight ? colourSel.allocated : colourUnSel.allocated,
										colourBG.allocated);
				}
			}
			x = xEnd;
			startSeg = endSeg;
		}
	}
}

int CallTip::PaintContents(Surface *surfaceWindow, bool draw) {
	PRectangle rcClientPos = wCallTip.GetClientPosition();
	PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left,
	                        rcClientPos.bottom - rcClientPos.top);
	PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1);

	// To make a nice small call tip window, it is only sized to fit most normal characters without accents
	int ascent = surfaceWindow->Ascent(font) - surfaceWindow->InternalLeading(font);

	// For each line...
	// Draw the definition in three parts: before highlight, highlighted, after highlight
	int ytext = rcClient.top + ascent + 1;
	rcClient.bottom = ytext + surfaceWindow->Descent(font) + 1;
	char *chunkVal = val;
	bool moreChunks = true;
	int maxWidth = 0;
	while (moreChunks) {
		char *chunkEnd = strchr(chunkVal, '\n');
		if (chunkEnd == NULL) {
			chunkEnd = chunkVal + strlen(chunkVal);
			moreChunks = false;
		}
		int chunkOffset = chunkVal - val;
		int chunkLength = chunkEnd - chunkVal;
		int chunkEndOffset = chunkOffset + chunkLength;
		int thisStartHighlight = Platform::Maximum(startHighlight, chunkOffset);
		thisStartHighlight = Platform::Minimum(thisStartHighlight, chunkEndOffset);
		thisStartHighlight -= chunkOffset;
		int thisEndHighlight = Platform::Maximum(endHighlight, chunkOffset);
		thisEndHighlight = Platform::Minimum(thisEndHighlight, chunkEndOffset);
		thisEndHighlight -= chunkOffset;
		rcClient.top = ytext - ascent - 1;

		int x = 5;

		DrawChunk(surfaceWindow, x, chunkVal, 0, thisStartHighlight,
			ytext, rcClient, false, draw);
		DrawChunk(surfaceWindow, x, chunkVal, thisStartHighlight, thisEndHighlight,
			ytext, rcClient, true, draw);
		DrawChunk(surfaceWindow, x, chunkVal, thisEndHighlight, chunkLength,
			ytext, rcClient, false, draw);

		chunkVal = chunkEnd + 1;
		ytext += lineHeight;
		rcClient.bottom += lineHeight;
		maxWidth = Platform::Maximum(maxWidth, x);
	}
	return maxWidth;
}

void CallTip::PaintCT(Surface *surfaceWindow) {
	if (!val)
		return;
	PRectangle rcClientPos = wCallTip.GetClientPosition();
	PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left,
	                        rcClientPos.bottom - rcClientPos.top);
	PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1);

	surfaceWindow->FillRectangle(rcClient, colourBG.allocated);

	offsetMain = 5;
	PaintContents(surfaceWindow, true);

	// Draw a raised border around the edges of the window
	surfaceWindow->MoveTo(0, rcClientSize.bottom - 1);
	surfaceWindow->PenColour(colourShade.allocated);
	surfaceWindow->LineTo(rcClientSize.right - 1, rcClientSize.bottom - 1);
	surfaceWindow->LineTo(rcClientSize.right - 1, 0);
	surfaceWindow->PenColour(colourLight.allocated);
	surfaceWindow->LineTo(0, 0);
	surfaceWindow->LineTo(0, rcClientSize.bottom - 1);
}

void CallTip::MouseClick(Point pt) {
	clickPlace = 0;
	if (pt.y < lineHeight) {
		if ((pt.x > xUp) && (pt.x < xUp + widthArrow - 2)) {
			clickPlace = 1;
		} else if ((pt.x > xDown) && (pt.x < xDown + widthArrow - 2)) {
			clickPlace = 2;
		}
	}
}

PRectangle CallTip::CallTipStart(int pos, Point pt, const char *defn,
                                 const char *faceName, int size,
                                 int codePage_, int characterSet, Window &wParent) {
	clickPlace = 0;
	if (val)
		delete []val;
	val = new char[strlen(defn) + 1];
	if (!val)
		return PRectangle();
	strcpy(val, defn);
	codePage = codePage_;
	Surface *surfaceMeasure = Surface::Allocate();
	if (!surfaceMeasure)
		return PRectangle();
	surfaceMeasure->Init(wParent.GetID());
	surfaceMeasure->SetUnicodeMode(SC_CP_UTF8 == codePage);
	surfaceMeasure->SetDBCSMode(codePage);
	startHighlight = 0;
	endHighlight = 0;
	inCallTipMode = true;
	posStartCallTip = pos;
	int deviceHeight = surfaceMeasure->DeviceHeightFont(size);
	font.Create(faceName, characterSet, deviceHeight, false, false);
	// Look for multiple lines in the text
	// Only support \n here - simply means container must avoid \r!
	int numLines = 1;
	const char *newline;
	const char *look = val;
	xUp = -100;
	xDown = -100;
	offsetMain = 5;
	int width = PaintContents(surfaceMeasure, false) + 5;
	while ((newline = strchr(look, '\n')) != NULL) {
		look = newline + 1;
		numLines++;
	}
	lineHeight = surfaceMeasure->Height(font);
	// Extra line for border and an empty line at top and bottom
	int height = lineHeight * numLines - surfaceMeasure->InternalLeading(font) + 2 + 2;
	delete surfaceMeasure;
	return PRectangle(pt.x - offsetMain, pt.y + 1, pt.x + width - offsetMain, pt.y + 1 + height);
}

void CallTip::CallTipCancel() {
	inCallTipMode = false;
	if (wCallTip.Created()) {
		wCallTip.Destroy();
	}
}

void CallTip::SetHighlight(int start, int end) {
	// Avoid flashing by checking something has really changed
	if ((start != startHighlight) || (end != endHighlight)) {
		startHighlight = start;
		endHighlight = end;
		if (wCallTip.Created()) {
			wCallTip.InvalidateAll();
		}
	}
}
<commit_msg>Fixed up and down button handling so they work when not on first line of calltip.<commit_after>// Scintilla source code edit control
/** @file CallTip.cxx
 ** Code for displaying call tips.
 **/
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

#include <stdlib.h>
#include <string.h>

#include "Platform.h"

#include "Scintilla.h"
#include "CallTip.h"

CallTip::CallTip() {
	wCallTip = 0;
	inCallTipMode = false;
	posStartCallTip = 0;
	val = 0;
	rectUp = PRectangle(0,0,0,0);
	rectDown = PRectangle(0,0,0,0);
	lineHeight = 1;
	startHighlight = 0;
	endHighlight = 0;

	colourBG.desired = ColourDesired(0xff, 0xff, 0xff);
	colourUnSel.desired = ColourDesired(0x80, 0x80, 0x80);
	colourSel.desired = ColourDesired(0, 0, 0x80);
	colourShade.desired = ColourDesired(0, 0, 0);
	colourLight.desired = ColourDesired(0xc0, 0xc0, 0xc0);
}

CallTip::~CallTip() {
	font.Release();
	wCallTip.Destroy();
	delete []val;
	val = 0;
}

const int widthArrow = 14;

void CallTip::RefreshColourPalette(Palette &pal, bool want) {
	pal.WantFind(colourBG, want);
	pal.WantFind(colourUnSel, want);
	pal.WantFind(colourSel, want);
	pal.WantFind(colourShade, want);
	pal.WantFind(colourLight, want);
}

static bool IsArrowCharacter(char ch) {
	return (ch == 0) || (ch == '\001') || (ch == '\002');
}

void CallTip::DrawChunk(Surface *surface, int &x, const char *s,
	int posStart, int posEnd, int ytext, PRectangle rcClient,
	bool highlight, bool draw) {
	s += posStart;
	int len = posEnd - posStart;
	int maxEnd = 0;
	int ends[10];
	for (int i=0;i<len;i++) {
		if (IsArrowCharacter(s[i])) {
			if (i > 0)
				ends[maxEnd++] = i;
			ends[maxEnd++] = i+1;
		}
	}
	ends[maxEnd++] = len;
	int startSeg = 0;
	int xEnd;
	for (int seg = 0; seg<maxEnd; seg++) {
		int endSeg = ends[seg];
		if (endSeg > startSeg) {
			if (IsArrowCharacter(s[startSeg])) {
				xEnd = x + widthArrow;
				offsetMain = xEnd;
				rcClient.left = x;
				rcClient.right = xEnd;
				if (draw) {
					const int halfWidth = widthArrow / 2 - 3;
					const int centreX = x + widthArrow / 2 - 1;
					const int centreY = (rcClient.top + rcClient.bottom) / 2;
					surface->FillRectangle(rcClient, colourBG.allocated);
					PRectangle rcClientInner(rcClient.left+1, rcClient.top+1, rcClient.right-2, rcClient.bottom-1);
					surface->FillRectangle(rcClientInner, colourUnSel.allocated);

					if (s[startSeg] == '\001') {
						// Up arrow
						Point pts[] = {
    						Point(centreX - halfWidth, centreY + halfWidth / 2),
    						Point(centreX + halfWidth, centreY + halfWidth / 2),
    						Point(centreX, centreY - halfWidth + halfWidth / 2),
						};
						surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
                 						colourBG.allocated, colourBG.allocated);
					} else {
						// Down arrow
						Point pts[] = {
    						Point(centreX - halfWidth, centreY - halfWidth / 2),
    						Point(centreX + halfWidth, centreY - halfWidth / 2),
    						Point(centreX, centreY + halfWidth - halfWidth / 2),
						};
						surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
                 						colourBG.allocated, colourBG.allocated);
					}
				} else {
					if (s[startSeg] == '\001') {
						rectUp = rcClient;
					} else {
						rectDown = rcClient;
					}
				}
			} else {
				xEnd = x + surface->WidthText(font, s+startSeg, endSeg - startSeg);
				if (draw) {
					rcClient.left = x;
					rcClient.right = xEnd;
					surface->DrawTextNoClip(rcClient, font, ytext,
										s+startSeg, endSeg - startSeg,
										highlight ? colourSel.allocated : colourUnSel.allocated,
										colourBG.allocated);
				}
			}
			x = xEnd;
			startSeg = endSeg;
		}
	}
}

int CallTip::PaintContents(Surface *surfaceWindow, bool draw) {
	PRectangle rcClientPos = wCallTip.GetClientPosition();
	PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left,
	                        rcClientPos.bottom - rcClientPos.top);
	PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1);

	// To make a nice small call tip window, it is only sized to fit most normal characters without accents
	int ascent = surfaceWindow->Ascent(font) - surfaceWindow->InternalLeading(font);

	// For each line...
	// Draw the definition in three parts: before highlight, highlighted, after highlight
	int ytext = rcClient.top + ascent + 1;
	rcClient.bottom = ytext + surfaceWindow->Descent(font) + 1;
	char *chunkVal = val;
	bool moreChunks = true;
	int maxWidth = 0;
	while (moreChunks) {
		char *chunkEnd = strchr(chunkVal, '\n');
		if (chunkEnd == NULL) {
			chunkEnd = chunkVal + strlen(chunkVal);
			moreChunks = false;
		}
		int chunkOffset = chunkVal - val;
		int chunkLength = chunkEnd - chunkVal;
		int chunkEndOffset = chunkOffset + chunkLength;
		int thisStartHighlight = Platform::Maximum(startHighlight, chunkOffset);
		thisStartHighlight = Platform::Minimum(thisStartHighlight, chunkEndOffset);
		thisStartHighlight -= chunkOffset;
		int thisEndHighlight = Platform::Maximum(endHighlight, chunkOffset);
		thisEndHighlight = Platform::Minimum(thisEndHighlight, chunkEndOffset);
		thisEndHighlight -= chunkOffset;
		rcClient.top = ytext - ascent - 1;

		int x = 5;

		DrawChunk(surfaceWindow, x, chunkVal, 0, thisStartHighlight,
			ytext, rcClient, false, draw);
		DrawChunk(surfaceWindow, x, chunkVal, thisStartHighlight, thisEndHighlight,
			ytext, rcClient, true, draw);
		DrawChunk(surfaceWindow, x, chunkVal, thisEndHighlight, chunkLength,
			ytext, rcClient, false, draw);

		chunkVal = chunkEnd + 1;
		ytext += lineHeight;
		rcClient.bottom += lineHeight;
		maxWidth = Platform::Maximum(maxWidth, x);
	}
	return maxWidth;
}

void CallTip::PaintCT(Surface *surfaceWindow) {
	if (!val)
		return;
	PRectangle rcClientPos = wCallTip.GetClientPosition();
	PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left,
	                        rcClientPos.bottom - rcClientPos.top);
	PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1);

	surfaceWindow->FillRectangle(rcClient, colourBG.allocated);

	offsetMain = 5;
	PaintContents(surfaceWindow, true);

	// Draw a raised border around the edges of the window
	surfaceWindow->MoveTo(0, rcClientSize.bottom - 1);
	surfaceWindow->PenColour(colourShade.allocated);
	surfaceWindow->LineTo(rcClientSize.right - 1, rcClientSize.bottom - 1);
	surfaceWindow->LineTo(rcClientSize.right - 1, 0);
	surfaceWindow->PenColour(colourLight.allocated);
	surfaceWindow->LineTo(0, 0);
	surfaceWindow->LineTo(0, rcClientSize.bottom - 1);
}

void CallTip::MouseClick(Point pt) {
	clickPlace = 0;
	if (rectUp.Contains(pt))
		clickPlace = 1;
	if (rectDown.Contains(pt))
		clickPlace = 2;
}

PRectangle CallTip::CallTipStart(int pos, Point pt, const char *defn,
                                 const char *faceName, int size,
                                 int codePage_, int characterSet, Window &wParent) {
	clickPlace = 0;
	if (val)
		delete []val;
	val = new char[strlen(defn) + 1];
	if (!val)
		return PRectangle();
	strcpy(val, defn);
	codePage = codePage_;
	Surface *surfaceMeasure = Surface::Allocate();
	if (!surfaceMeasure)
		return PRectangle();
	surfaceMeasure->Init(wParent.GetID());
	surfaceMeasure->SetUnicodeMode(SC_CP_UTF8 == codePage);
	surfaceMeasure->SetDBCSMode(codePage);
	startHighlight = 0;
	endHighlight = 0;
	inCallTipMode = true;
	posStartCallTip = pos;
	int deviceHeight = surfaceMeasure->DeviceHeightFont(size);
	font.Create(faceName, characterSet, deviceHeight, false, false);
	// Look for multiple lines in the text
	// Only support \n here - simply means container must avoid \r!
	int numLines = 1;
	const char *newline;
	const char *look = val;
	rectUp = PRectangle(0,0,0,0);
	rectDown = PRectangle(0,0,0,0);
	offsetMain = 5;
	int width = PaintContents(surfaceMeasure, false) + 5;
	while ((newline = strchr(look, '\n')) != NULL) {
		look = newline + 1;
		numLines++;
	}
	lineHeight = surfaceMeasure->Height(font);
	// Extra line for border and an empty line at top and bottom
	int height = lineHeight * numLines - surfaceMeasure->InternalLeading(font) + 2 + 2;
	delete surfaceMeasure;
	return PRectangle(pt.x - offsetMain, pt.y + 1, pt.x + width - offsetMain, pt.y + 1 + height);
}

void CallTip::CallTipCancel() {
	inCallTipMode = false;
	if (wCallTip.Created()) {
		wCallTip.Destroy();
	}
}

void CallTip::SetHighlight(int start, int end) {
	// Avoid flashing by checking something has really changed
	if ((start != startHighlight) || (end != endHighlight)) {
		startHighlight = start;
		endHighlight = end;
		if (wCallTip.Created()) {
			wCallTip.InvalidateAll();
		}
	}
}
<|endoftext|>


<commit_before>/* Copyright 2016 The TensorFlow Authors. All Rights Reserved.

Licensed 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 <zlib.h>

#include "tensorflow/core/lib/io/zlib_inputstream.h"

#include "tensorflow/core/lib/strings/strcat.h"
#include "tensorflow/core/platform/logging.h"

namespace tensorflow {
namespace io {

struct ZStreamDef {
  ZStreamDef(size_t input_buffer_capacity, size_t output_buffer_capacity)
      : input(new Bytef[input_buffer_capacity]),
        output(new Bytef[output_buffer_capacity]),
        stream(new z_stream) {}

  // Buffer for storing contents read from compressed stream.
  // TODO(srbs): Consider using circular buffers. That would greatly simplify
  // the implementation.
  std::unique_ptr<Bytef[]> input;

  // Buffer for storing inflated contents of `input_stream_`.
  std::unique_ptr<Bytef[]> output;

  // Configuration passed to `inflate`.
  //
  // z_stream_def_->stream->next_in:
  //   Next byte to de-compress. Points to some byte in
  //   z_stream_def_->streamdef_.input buffer.
  // z_stream_def_->stream->avail_in:
  //   Number of bytes available to be decompressed at this time.
  // z_stream_def_->stream->next_out:
  //   Next byte to write de-compressed data to. Points to some byte in
  //   z_stream_def_->streamdef_.output buffer.
  // z_stream_def_->stream->avail_out:
  //   Number of free bytes available at write location.
  std::unique_ptr<z_stream> stream;
};

ZlibInputStream::ZlibInputStream(
    InputStreamInterface* input_stream,
    size_t input_buffer_bytes,   // size of z_stream.next_in buffer
    size_t output_buffer_bytes,  // size of z_stream.next_out buffer
    const ZlibCompressionOptions& zlib_options, bool owns_input_stream)
    : owns_input_stream_(owns_input_stream),
      input_stream_(input_stream),
      input_buffer_capacity_(input_buffer_bytes),
      output_buffer_capacity_(output_buffer_bytes),
      zlib_options_(zlib_options),
      z_stream_def_(
          new ZStreamDef(input_buffer_capacity_, output_buffer_capacity_)),
      bytes_read_(0) {
  InitZlibBuffer();
}

ZlibInputStream::ZlibInputStream(InputStreamInterface* input_stream,
                                 size_t input_buffer_bytes,
                                 size_t output_buffer_bytes,
                                 const ZlibCompressionOptions& zlib_options)
    : ZlibInputStream(input_stream, input_buffer_bytes, output_buffer_bytes,
                      zlib_options, false) {}

ZlibInputStream::~ZlibInputStream() {
  if (z_stream_def_->stream) {
    inflateEnd(z_stream_def_->stream.get());
  }
  if (owns_input_stream_) {
    delete input_stream_;
  }
}

Status ZlibInputStream::Reset() {
  TF_RETURN_IF_ERROR(input_stream_->Reset());
  inflateEnd(z_stream_def_->stream.get());
  InitZlibBuffer();
  bytes_read_ = 0;
  return Status::OK();
}

void ZlibInputStream::InitZlibBuffer() {
  memset(z_stream_def_->stream.get(), 0, sizeof(z_stream));

  z_stream_def_->stream->zalloc = Z_NULL;
  z_stream_def_->stream->zfree = Z_NULL;
  z_stream_def_->stream->opaque = Z_NULL;
  z_stream_def_->stream->next_in = Z_NULL;
  z_stream_def_->stream->avail_in = 0;

  int status =
      inflateInit2(z_stream_def_->stream.get(), zlib_options_.window_bits);

  CHECK_EQ(status, Z_OK) << "inflateInit failed with status " << status;

  z_stream_def_->stream->next_in = z_stream_def_->input.get();
  z_stream_def_->stream->next_out = z_stream_def_->output.get();
  next_unread_byte_ = reinterpret_cast<char*>(z_stream_def_->output.get());
  z_stream_def_->stream->avail_in = 0;
  z_stream_def_->stream->avail_out = output_buffer_capacity_;
}

Status ZlibInputStream::ReadFromStream() {
  int bytes_to_read = input_buffer_capacity_;
  char* read_location = reinterpret_cast<char*>(z_stream_def_->input.get());

  // If there are unread bytes in the input stream we move them to the head
  // of the stream to maximize the space available to read new data into.
  if (z_stream_def_->stream->avail_in > 0) {
    uLong read_bytes =
        z_stream_def_->stream->next_in - z_stream_def_->input.get();
    // Remove `read_bytes` from the head of the input stream.
    // Move unread bytes to the head of the input stream.
    if (read_bytes > 0) {
      memmove(z_stream_def_->input.get(), z_stream_def_->stream->next_in,
              z_stream_def_->stream->avail_in);
    }

    bytes_to_read -= z_stream_def_->stream->avail_in;
    read_location += z_stream_def_->stream->avail_in;
  }
  string data;
  // Try to read enough data to fill up z_stream_def_->input.
  // TODO(rohanj): Add a char* version of ReadNBytes to InputStreamInterface
  // and use that instead to make this more efficient.
  Status s = input_stream_->ReadNBytes(bytes_to_read, &data);
  memcpy(read_location, data.data(), data.size());

  // Since we moved unread data to the head of the input stream we can point
  // next_in to the head of the input stream.
  z_stream_def_->stream->next_in = z_stream_def_->input.get();

  // Note: data.size() could be different from bytes_to_read.
  z_stream_def_->stream->avail_in += data.size();

  if (!s.ok() && !errors::IsOutOfRange(s)) {
    return s;
  }

  // We throw OutOfRange error iff no new data has been read from stream.
  // Since we never check how much data is remaining in the stream, it is
  // possible that on the last read there isn't enough data in the stream to
  // fill up the buffer in which case input_stream_->ReadNBytes would return an
  // OutOfRange error.
  if (data.empty()) {
    return errors::OutOfRange("EOF reached");
  }
  if (errors::IsOutOfRange(s)) {
    return Status::OK();
  }

  return s;
}

size_t ZlibInputStream::ReadBytesFromCache(size_t bytes_to_read,
                                           string* result) {
  size_t unread_bytes =
      reinterpret_cast<char*>(z_stream_def_->stream->next_out) -
      next_unread_byte_;
  size_t can_read_bytes = std::min(bytes_to_read, unread_bytes);
  if (can_read_bytes > 0) {
    result->append(next_unread_byte_, can_read_bytes);
    next_unread_byte_ += can_read_bytes;
  }
  bytes_read_ += can_read_bytes;
  return can_read_bytes;
}

size_t ZlibInputStream::NumUnreadBytes() const {
  size_t read_bytes =
      next_unread_byte_ - reinterpret_cast<char*>(z_stream_def_->output.get());
  return output_buffer_capacity_ - z_stream_def_->stream->avail_out -
         read_bytes;
}

Status ZlibInputStream::ReadNBytes(int64 bytes_to_read, string* result) {
  result->clear();
  // Read as many bytes as possible from cache.
  bytes_to_read -= ReadBytesFromCache(bytes_to_read, result);

  while (bytes_to_read > 0) {
    // At this point we can be sure that cache has been emptied.
    DCHECK_EQ(NumUnreadBytes(), 0);

    // Now that the cache is empty we need to inflate more data.

    // Step 1. Fill up input buffer.
    // We read from stream only after the previously read contents have been
    // completely consumed. This is an optimization and can be removed if
    // it causes problems. `ReadFromStream` is capable of handling partially
    // filled up buffers.
    if (z_stream_def_->stream->avail_in == 0) {
      TF_RETURN_IF_ERROR(ReadFromStream());
    }

    // Step 2. Setup output stream.
    z_stream_def_->stream->next_out = z_stream_def_->output.get();
    next_unread_byte_ = reinterpret_cast<char*>(z_stream_def_->output.get());
    z_stream_def_->stream->avail_out = output_buffer_capacity_;

    // Step 3. Inflate Inflate Inflate!
    TF_RETURN_IF_ERROR(Inflate());

    bytes_to_read -= ReadBytesFromCache(bytes_to_read, result);
  }

  return Status::OK();
}

int64 ZlibInputStream::Tell() const { return bytes_read_; }

Status ZlibInputStream::Inflate() {
  int error = inflate(z_stream_def_->stream.get(), zlib_options_.flush_mode);
  if (error != Z_OK && error != Z_STREAM_END) {
    string error_string =
        strings::StrCat("inflate() failed with error ", error);
    if (z_stream_def_->stream->msg != nullptr) {
      strings::StrAppend(&error_string, ": ", z_stream_def_->stream->msg);
    }
    return errors::DataLoss(error_string);
  }
  return Status::OK();
}

}  // namespace io
}  // namespace tensorflow
<commit_msg>Fix bug in zlib_inputstream Inflate. If no output is generated after call to Inflate, read data from input stream. Earlier we would wait for zlib to consume all data before reading more input data. That assumption was incorrect. zlib can leave unread data in the input buffer if it is unable to make any progress.<commit_after>/* Copyright 2016 The TensorFlow Authors. All Rights Reserved.

Licensed 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 <zlib.h>

#include "tensorflow/core/lib/io/zlib_inputstream.h"

#include "tensorflow/core/lib/strings/strcat.h"
#include "tensorflow/core/platform/logging.h"

namespace tensorflow {
namespace io {

struct ZStreamDef {
  ZStreamDef(size_t input_buffer_capacity, size_t output_buffer_capacity)
      : input(new Bytef[input_buffer_capacity]),
        output(new Bytef[output_buffer_capacity]),
        stream(new z_stream) {}

  // Buffer for storing contents read from compressed stream.
  // TODO(srbs): Consider using circular buffers. That would greatly simplify
  // the implementation.
  std::unique_ptr<Bytef[]> input;

  // Buffer for storing inflated contents of `input_stream_`.
  std::unique_ptr<Bytef[]> output;

  // Configuration passed to `inflate`.
  //
  // z_stream_def_->stream->next_in:
  //   Next byte to de-compress. Points to some byte in
  //   z_stream_def_->streamdef_.input buffer.
  // z_stream_def_->stream->avail_in:
  //   Number of bytes available to be decompressed at this time.
  // z_stream_def_->stream->next_out:
  //   Next byte to write de-compressed data to. Points to some byte in
  //   z_stream_def_->streamdef_.output buffer.
  // z_stream_def_->stream->avail_out:
  //   Number of free bytes available at write location.
  std::unique_ptr<z_stream> stream;
};

ZlibInputStream::ZlibInputStream(
    InputStreamInterface* input_stream,
    size_t input_buffer_bytes,   // size of z_stream.next_in buffer
    size_t output_buffer_bytes,  // size of z_stream.next_out buffer
    const ZlibCompressionOptions& zlib_options, bool owns_input_stream)
    : owns_input_stream_(owns_input_stream),
      input_stream_(input_stream),
      input_buffer_capacity_(input_buffer_bytes),
      output_buffer_capacity_(output_buffer_bytes),
      zlib_options_(zlib_options),
      z_stream_def_(
          new ZStreamDef(input_buffer_capacity_, output_buffer_capacity_)),
      bytes_read_(0) {
  InitZlibBuffer();
}

ZlibInputStream::ZlibInputStream(InputStreamInterface* input_stream,
                                 size_t input_buffer_bytes,
                                 size_t output_buffer_bytes,
                                 const ZlibCompressionOptions& zlib_options)
    : ZlibInputStream(input_stream, input_buffer_bytes, output_buffer_bytes,
                      zlib_options, false) {}

ZlibInputStream::~ZlibInputStream() {
  if (z_stream_def_->stream) {
    inflateEnd(z_stream_def_->stream.get());
  }
  if (owns_input_stream_) {
    delete input_stream_;
  }
}

Status ZlibInputStream::Reset() {
  TF_RETURN_IF_ERROR(input_stream_->Reset());
  inflateEnd(z_stream_def_->stream.get());
  InitZlibBuffer();
  bytes_read_ = 0;
  return Status::OK();
}

void ZlibInputStream::InitZlibBuffer() {
  memset(z_stream_def_->stream.get(), 0, sizeof(z_stream));

  z_stream_def_->stream->zalloc = Z_NULL;
  z_stream_def_->stream->zfree = Z_NULL;
  z_stream_def_->stream->opaque = Z_NULL;
  z_stream_def_->stream->next_in = Z_NULL;
  z_stream_def_->stream->avail_in = 0;

  int status =
      inflateInit2(z_stream_def_->stream.get(), zlib_options_.window_bits);

  CHECK_EQ(status, Z_OK) << "inflateInit failed with status " << status;

  z_stream_def_->stream->next_in = z_stream_def_->input.get();
  z_stream_def_->stream->next_out = z_stream_def_->output.get();
  next_unread_byte_ = reinterpret_cast<char*>(z_stream_def_->output.get());
  z_stream_def_->stream->avail_in = 0;
  z_stream_def_->stream->avail_out = output_buffer_capacity_;
}

Status ZlibInputStream::ReadFromStream() {
  int bytes_to_read = input_buffer_capacity_;
  char* read_location = reinterpret_cast<char*>(z_stream_def_->input.get());

  // If there are unread bytes in the input stream we move them to the head
  // of the stream to maximize the space available to read new data into.
  if (z_stream_def_->stream->avail_in > 0) {
    uLong read_bytes =
        z_stream_def_->stream->next_in - z_stream_def_->input.get();
    // Remove `read_bytes` from the head of the input stream.
    // Move unread bytes to the head of the input stream.
    if (read_bytes > 0) {
      memmove(z_stream_def_->input.get(), z_stream_def_->stream->next_in,
              z_stream_def_->stream->avail_in);
    }

    bytes_to_read -= z_stream_def_->stream->avail_in;
    read_location += z_stream_def_->stream->avail_in;
  }
  string data;
  // Try to read enough data to fill up z_stream_def_->input.
  // TODO(rohanj): Add a char* version of ReadNBytes to InputStreamInterface
  // and use that instead to make this more efficient.
  Status s = input_stream_->ReadNBytes(bytes_to_read, &data);
  memcpy(read_location, data.data(), data.size());

  // Since we moved unread data to the head of the input stream we can point
  // next_in to the head of the input stream.
  z_stream_def_->stream->next_in = z_stream_def_->input.get();

  // Note: data.size() could be different from bytes_to_read.
  z_stream_def_->stream->avail_in += data.size();

  if (!s.ok() && !errors::IsOutOfRange(s)) {
    return s;
  }

  // We throw OutOfRange error iff no new data has been read from stream.
  // Since we never check how much data is remaining in the stream, it is
  // possible that on the last read there isn't enough data in the stream to
  // fill up the buffer in which case input_stream_->ReadNBytes would return an
  // OutOfRange error.
  if (data.empty()) {
    return errors::OutOfRange("EOF reached");
  }
  if (errors::IsOutOfRange(s)) {
    return Status::OK();
  }

  return s;
}

size_t ZlibInputStream::ReadBytesFromCache(size_t bytes_to_read,
                                           string* result) {
  size_t unread_bytes =
      reinterpret_cast<char*>(z_stream_def_->stream->next_out) -
      next_unread_byte_;
  size_t can_read_bytes = std::min(bytes_to_read, unread_bytes);
  if (can_read_bytes > 0) {
    result->append(next_unread_byte_, can_read_bytes);
    next_unread_byte_ += can_read_bytes;
  }
  bytes_read_ += can_read_bytes;
  return can_read_bytes;
}

size_t ZlibInputStream::NumUnreadBytes() const {
  size_t read_bytes =
      next_unread_byte_ - reinterpret_cast<char*>(z_stream_def_->output.get());
  return output_buffer_capacity_ - z_stream_def_->stream->avail_out -
         read_bytes;
}

Status ZlibInputStream::ReadNBytes(int64 bytes_to_read, string* result) {
  result->clear();
  // Read as many bytes as possible from cache.
  bytes_to_read -= ReadBytesFromCache(bytes_to_read, result);

  while (bytes_to_read > 0) {
    // At this point we can be sure that cache has been emptied.
    DCHECK_EQ(NumUnreadBytes(), 0);

    // Now that the cache is empty we need to inflate more data.

    // Step 1. Setup output stream.
    z_stream_def_->stream->next_out = z_stream_def_->output.get();
    next_unread_byte_ = reinterpret_cast<char*>(z_stream_def_->output.get());
    z_stream_def_->stream->avail_out = output_buffer_capacity_;

    // Step 2. Try to inflate some input data.
    TF_RETURN_IF_ERROR(Inflate());

    // Step 3. Read any data produced by inflate. If no progress was made by
    // inflate, read more compressed data from the input stream.
    if (NumUnreadBytes() == 0) {
      TF_RETURN_IF_ERROR(ReadFromStream());
    } else {
      bytes_to_read -= ReadBytesFromCache(bytes_to_read, result);
    }
  }

  return Status::OK();
}

int64 ZlibInputStream::Tell() const { return bytes_read_; }

Status ZlibInputStream::Inflate() {
  int error = inflate(z_stream_def_->stream.get(), zlib_options_.flush_mode);
  // Source: http://zlib.net/manual.html
  // Z_BUF_ERROR: `inflate` returns Z_BUF_ERROR if no progress was made. This is
  // not fatal and `inflate` can be called again with more input and output
  // space to continue inflating.
  if (error != Z_OK && error != Z_STREAM_END && error != Z_BUF_ERROR) {
    string error_string =
        strings::StrCat("inflate() failed with error ", error);
    if (z_stream_def_->stream->msg != nullptr) {
      strings::StrAppend(&error_string, ": ", z_stream_def_->stream->msg);
    }
    return errors::DataLoss(error_string);
  }
  return Status::OK();
}

}  // namespace io
}  // namespace tensorflow
<|endoftext|>


<commit_before>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

Licensed 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 <cstdint>
#include <cstdlib>

#include "tensorflow/core/platform/base64.h"
#include "tensorflow/core/platform/status.h"
#include "tensorflow/core/platform/stringpiece.h"

// This is a fuzzer for tensorflow::Base64Encode and tensorflow::Base64Decode.

namespace {

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
  uint8_t *byte_data = const_cast<uint8_t *>(data);
  char *char_data = reinterpret_cast<char *>(byte_data);

  tensorflow::StringPiece original_sp =
      tensorflow::StringPiece(char_data, size);

  string encoded_string;
  string decoded_string;
  tensorflow::Status s;
  s = tensorflow::Base64Encode(original_sp, &encoded_string);
  assert(s.ok());
  s = tensorflow::Base64Decode(encoded_string, &decoded_string);
  assert(s.ok());
  assert(original_sp == decoded_string);

  return 0;
}

}  // namespace
<commit_msg>Fix `base64_fuzz` as it currently breaks on OSSFuzz.<commit_after>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

Licensed 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 <cstdint>
#include <cstdlib>

#include "tensorflow/core/platform/base64.h"
#include "tensorflow/core/platform/status.h"
#include "tensorflow/core/platform/stringpiece.h"

// This is a fuzzer for tensorflow::Base64Encode and tensorflow::Base64Decode.

namespace {

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
  std::string input(reinterpret_cast<const char *>(data));
  std::string encoded_string;
  std::string decoded_string;
  tensorflow::Status s;
  s = tensorflow::Base64Encode(input, &encoded_string);
  assert(s.ok());
  s = tensorflow::Base64Decode(encoded_string, &decoded_string);
  assert(s.ok());
  assert(input == decoded_string);

  return 0;
}

}  // namespace
<|endoftext|>


<commit_before>#pragma once

#include "Singleton.hpp"
#include "types.hpp"
#include "Callback.hpp"

#include <string>
#include <atomic>

#include <json.hpp>

#undef SendMessage // Windows at its finest

using json = nlohmann::json;

class Channel
{
	friend class ChannelManager;
public:
	enum class Type : unsigned int
	{
		GUILD_TEXT = 0,
		DM = 1,
		GUILD_VOICE = 2,
		GROUP_DM = 3,
		GUILD_CATEGORY = 4
	};

public:
	Channel(ChannelId_t pawn_id, json const &data, GuildId_t guild_id);
	~Channel() = default;

private:
	const ChannelId_t m_PawnId;

	Snowflake_t m_Id;

	GuildId_t m_GuildId = INVALID_GUILD_ID;

	Type m_Type;

	std::string
		m_Name,
		m_Topic;

	int m_Position = -1;
	bool m_IsNsfw = false;

	ChannelId_t m_ParentId = INVALID_CHANNEL_ID;

public:
	inline ChannelId_t GetPawnId() const
	{
		return m_PawnId;
	}
	inline Snowflake_t const &GetId() const
	{
		return m_Id;
	}
	inline Type GetType() const
	{
		return m_Type;
	}
	inline GuildId_t GetGuildId() const
	{
		return m_GuildId;
	}
	inline std::string const &GetName() const
	{
		return m_Name;
	}
	inline std::string const &GetTopic() const
	{
		return m_Topic;
	}
	inline int GetPosition() const
	{
		return m_Position;
	}
	inline bool IsNsfw() const
	{
		return m_IsNsfw;
	}
	inline ChannelId_t GetParentId() const
	{
		return m_ParentId;
	}

	void SendMessage(std::string &&msg, pawn_cb::Callback_t &&cb);
	void SetChannelName(std::string const &name);
	void SetChannelTopic(std::string const &topic);
	void SetChannelPosition(int const position);
	void SetChannelNsfw(bool const is_nsfw);
	void SetChannelParentCategory(Channel_t const &parent);
	void DeleteChannel();
};

class ChannelManager : public Singleton<ChannelManager>
{
	friend class Singleton<ChannelManager>;
private:
	ChannelManager() = default;
	~ChannelManager() = default;

private:
	const unsigned int m_InitValue = 1;
	std::atomic<unsigned int> m_Initialized{ 0 };

	std::map<ChannelId_t, Channel_t> m_Channels; //PAWN channel-id to actual channel map
	ChannelId_t m_CreatedChannelId = INVALID_CHANNEL_ID;

public:
	void Initialize();
	bool IsInitialized();

	bool CreateGuildChannel(Guild_t const &guild,
		std::string const &name, Channel::Type type, pawn_cb::Callback_t &&callback);
	ChannelId_t GetCreatedGuildChannelId() const
	{
		return m_CreatedChannelId;
	}

	ChannelId_t AddChannel(json const &data, GuildId_t guild_id = 0);
	void UpdateChannel(json const &data);
	void DeleteChannel(json const &data);

	Channel_t const &FindChannel(ChannelId_t id);
	Channel_t const &FindChannelByName(std::string const &name);
	Channel_t const &FindChannelById(Snowflake_t const &sfid);
};
<commit_msg>Add parent_id, make per-channel Update and update ch. types<commit_after>#pragma once

#include "Singleton.hpp"
#include "types.hpp"
#include "Callback.hpp"

#include <string>
#include <atomic>

#include <json.hpp>

#undef SendMessage // Windows at its finest

using json = nlohmann::json;

class Channel
{
	friend class ChannelManager;
public:
	enum class Type : unsigned int
	{
		GUILD_TEXT = 0,
		DM = 1,
		GUILD_VOICE = 2,
		GROUP_DM = 3,
		GUILD_CATEGORY = 4,
		GUILD_NEWS = 5,
		GUILD_STORE = 6
	};

public:
	Channel(ChannelId_t pawn_id, json const &data, GuildId_t guild_id);
	~Channel() = default;

private:
	const ChannelId_t m_PawnId;

	Snowflake_t m_Id;

	GuildId_t m_GuildId = INVALID_GUILD_ID;

	Type m_Type;

	std::string
		m_Name,
		m_Topic;

	int m_Position = -1;
	bool m_IsNsfw = false;

	ChannelId_t m_ParentId = INVALID_CHANNEL_ID;

public:
	inline ChannelId_t GetPawnId() const
	{
		return m_PawnId;
	}
	inline Snowflake_t const &GetId() const
	{
		return m_Id;
	}
	inline Type GetType() const
	{
		return m_Type;
	}
	inline GuildId_t GetGuildId() const
	{
		return m_GuildId;
	}
	inline std::string const &GetName() const
	{
		return m_Name;
	}
	inline std::string const &GetTopic() const
	{
		return m_Topic;
	}
	inline int GetPosition() const
	{
		return m_Position;
	}
	inline bool IsNsfw() const
	{
		return m_IsNsfw;
	}
	inline ChannelId_t GetParentId() const
	{
		return m_ParentId;
	}

	void SendMessage(std::string &&msg, pawn_cb::Callback_t &&cb);
	void SetChannelName(std::string const &name);
	void SetChannelTopic(std::string const &topic);
	void SetChannelPosition(int const position);
	void SetChannelNsfw(bool const is_nsfw);
	void SetChannelParentCategory(Channel_t const &parent);
	void DeleteChannel();
	void Update(json const &data);
};

class ChannelManager : public Singleton<ChannelManager>
{
	friend class Singleton<ChannelManager>;
private:
	ChannelManager() = default;
	~ChannelManager() = default;

private:
	const unsigned int m_InitValue = 1;
	std::atomic<unsigned int> m_Initialized{ 0 };

	std::map<ChannelId_t, Channel_t> m_Channels; //PAWN channel-id to actual channel map
	ChannelId_t m_CreatedChannelId = INVALID_CHANNEL_ID;

public:
	void Initialize();
	bool IsInitialized();

	bool CreateGuildChannel(Guild_t const &guild,
		std::string const &name, Channel::Type type, pawn_cb::Callback_t &&callback);
	ChannelId_t GetCreatedGuildChannelId() const
	{
		return m_CreatedChannelId;
	}

	ChannelId_t AddChannel(json const &data, GuildId_t guild_id = 0);
	void DeleteChannel(json const &data);

	Channel_t const &FindChannel(ChannelId_t id);
	Channel_t const &FindChannelByName(std::string const &name);
	Channel_t const &FindChannelById(Snowflake_t const &sfid);
};
<|endoftext|>


<commit_before>#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>

#include "Command.h"
#include "DynamicKomi.h"
#include "GoBoard.h"
#include "Message.h"
#include "UctSearch.h"
#include "ZobristHash.h"

using namespace std;

////////////
//  定数  //
////////////

//  コマンド
const string command[COMMAND_MAX] = {
  "--playout",
  "--time",
  "--size",
  "--const-time",
  "--thread",
  "--komi",
  "--handicap",
  "--reuse-subtree",
  "--pondering",
  "--tree-size",
  "--no-debug",
  "--superko",
};

//  コマンドの説明
const string errmessage[COMMAND_MAX] = {
  "Set playouts",
  "Set all thinking time",
  "Set board size",
  "Set mode const time, and set thinking time per move",
  "Set threads",
  "Set komi",
  "Set the number of handicap stones (for testing)",
  "Reuse subtree",
  "Set pondering mode",
  "Set tree size (tree size must be 2 ^ n)",
  "Prohibit any debug message",
  "Prohibit superko move",
};

//////////////////////
//  コマンドの処理  //
//////////////////////
void
AnalyzeCommand( int argc, char **argv )
{
  int n, size;

  for (int i = 1; i < argc; i++){
    n = COMMAND_MAX + 1;
    for (int j = 0; j < COMMAND_MAX; j++){
      if (!strcmp(argv[i], command[j].c_str())){
	n = j;
      }
    }

    switch (n) {
      case COMMAND_PLAYOUT:
	// プレイアウト数固定の探索の設定
	SetPlayout(atoi(argv[++i]));
	SetMode(CONST_PLAYOUT_MODE);
	break;
      case COMMAND_TIME:
	// 持ち時間の設定
	SetTime(atof(argv[++i]));
	SetMode(TIME_SETTING_MODE);
	break;
      case COMMAND_SIZE:
	// 碁盤の大きさの設定
	i++;
	size = atoi(argv[i]);
	if (pure_board_size != size &&
	    size > 0 && size <= PURE_BOARD_SIZE) {
	  SetBoardSize(size);
	  SetParameter();
	}
	break;
      case COMMAND_CONST_TIME:
	// 1手あたりの思考時間を固定した探索の設定
	SetConstTime(atof(argv[++i]));
	SetMode(CONST_TIME_MODE);
	break;
      case COMMAND_THREAD:
	// 探索スレッド数の設定
	SetThread(atoi(argv[++i]));
	break;
      case COMMAND_KOMI:
	// コミの設定
	SetKomi(atof(argv[++i]));
	break;
      case COMMAND_HANDICAP:
	// 置き石の個数の設定
	SetConstHandicapNum(atoi(argv[++i]));
	SetHandicapNum(0);
	break;
      case COMMAND_REUSE_SUBTREE:
	// 探索結果の再利用の設定
        SetReuseSubtree(true);
        break;
      case COMMAND_PONDERING :
	// 予測読みの設定
	SetReuseSubtree(true);
	SetPonderingMode(true);
	break;
      case COMMAND_TREE_SIZE:
	// UCTのノードの個数の設定
	SetHashSize((unsigned int)atoi(argv[++i]));
	break;
      case COMMAND_SUPERKO:
	// 超劫のh判定の設定
	SetSuperKo(true);
	break;
      case COMMAND_NO_DEBUG:
	// デバッグメッセージを出力しない設定
	SetDebugMessageMode(false);
	break;
      default:
	for (int j = 0; j < COMMAND_MAX; j++){
	  fprintf(stderr, "%-22s : %s\n", command[j].c_str(), errmessage[j].c_str());
	}
	exit(1);
    }
  }

}
<commit_msg>Refactoring<commit_after>#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>

#include "Command.h"
#include "DynamicKomi.h"
#include "GoBoard.h"
#include "Message.h"
#include "UctSearch.h"
#include "ZobristHash.h"

using namespace std;

////////////
//  定数  //
////////////

//  コマンド
const string command[COMMAND_MAX] = {
  "--playout",
  "--time",
  "--size",
  "--const-time",
  "--thread",
  "--komi",
  "--handicap",
  "--reuse-subtree",
  "--pondering",
  "--tree-size",
  "--no-debug",
  "--superko",
};

//  コマンドの説明
const string errmessage[COMMAND_MAX] = {
  "Set playouts",
  "Set all thinking time",
  "Set board size",
  "Set mode const time, and set thinking time per move",
  "Set threads",
  "Set komi",
  "Set the number of handicap stones (for testing)",
  "Reuse subtree",
  "Set pondering mode",
  "Set tree size (tree size must be 2 ^ n)",
  "Prohibit any debug message",
  "Prohibit superko move",
};

//////////////////////
//  コマンドの処理  //
//////////////////////
void
AnalyzeCommand( int argc, char **argv )
{
  int n, size;

  for (int i = 1; i < argc; i++){
    n = COMMAND_MAX + 1;
    for (int j = 0; j < COMMAND_MAX; j++){
      if (!strcmp(argv[i], command[j].c_str())){
	n = j;
      }
    }

    switch (n) {
      case COMMAND_PLAYOUT:
	// プレイアウト数固定の探索の設定
	SetPlayout(atoi(argv[++i]));
	SetMode(CONST_PLAYOUT_MODE);
	break;
      case COMMAND_TIME:
	// 持ち時間の設定
	SetTime(atof(argv[++i]));
	SetMode(TIME_SETTING_MODE);
	break;
      case COMMAND_SIZE:
	// 碁盤の大きさの設定
	size = atoi(argv[++i]);
	if (pure_board_size != size &&
	    size > 0 && size <= PURE_BOARD_SIZE) {
	  SetBoardSize(size);
	  SetParameter();
	}
	break;
      case COMMAND_CONST_TIME:
	// 1手あたりの思考時間を固定した探索の設定
	SetConstTime(atof(argv[++i]));
	SetMode(CONST_TIME_MODE);
	break;
      case COMMAND_THREAD:
	// 探索スレッド数の設定
	SetThread(atoi(argv[++i]));
	break;
      case COMMAND_KOMI:
	// コミの設定
	SetKomi(atof(argv[++i]));
	break;
      case COMMAND_HANDICAP:
	// 置き石の個数の設定
	SetConstHandicapNum(atoi(argv[++i]));
	SetHandicapNum(0);
	break;
      case COMMAND_REUSE_SUBTREE:
	// 探索結果の再利用の設定
        SetReuseSubtree(true);
        break;
      case COMMAND_PONDERING :
	// 予測読みの設定
	SetReuseSubtree(true);
	SetPonderingMode(true);
	break;
      case COMMAND_TREE_SIZE:
	// UCTのノードの個数の設定
	SetHashSize((unsigned int)atoi(argv[++i]));
	break;
      case COMMAND_SUPERKO:
	// 超劫のh判定の設定
	SetSuperKo(true);
	break;
      case COMMAND_NO_DEBUG:
	// デバッグメッセージを出力しない設定
	SetDebugMessageMode(false);
	break;
      default:
	for (int j = 0; j < COMMAND_MAX; j++){
	  fprintf(stderr, "%-22s : %s\n", command[j].c_str(), errmessage[j].c_str());
	}
	exit(1);
    }
  }

}
<|endoftext|>


<commit_before>#include "curl_fun.h"
#include "parameters.h"
#include "curl/curl.h"
#include "jansson.h"
#include <unistd.h>

size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
  ((std::string*)userp)->append((char*)contents, size * nmemb);
  return size * nmemb;
}

json_t* getJsonFromUrl(Parameters& params, std::string url, std::string postFields,
                       bool getRequest)
{
  if (!params.cacert.empty())
    curl_easy_setopt(params.curl, CURLOPT_CAINFO, params.cacert.c_str());

  curl_easy_setopt(params.curl, CURLOPT_URL, url.c_str());
  curl_easy_setopt(params.curl, CURLOPT_CONNECTTIMEOUT, 10L);
  curl_easy_setopt(params.curl, CURLOPT_TIMEOUT, 20L);
  curl_easy_setopt(params.curl, CURLOPT_ENCODING, "gzip");
  if (!postFields.empty()) {
    curl_easy_setopt(params.curl, CURLOPT_POSTFIELDS, postFields.c_str());
  }
  curl_easy_setopt(params.curl, CURLOPT_WRITEFUNCTION, WriteCallback);
  std::string readBuffer;
  curl_easy_setopt(params.curl, CURLOPT_WRITEDATA, &readBuffer);
  curl_easy_setopt(params.curl, CURLOPT_DNS_CACHE_TIMEOUT, 3600);
  // Some calls must be GET requests
  if (getRequest) {
    curl_easy_setopt(params.curl, CURLOPT_CUSTOMREQUEST, "GET");
  }
  CURLcode resCurl = curl_easy_perform(params.curl);
  while (resCurl != CURLE_OK) {
    *params.logFile << "Error with cURL: " << curl_easy_strerror(resCurl) << std::endl;
    *params.logFile << "  URL: " << url << std::endl;
    *params.logFile << "  Retry in 2 sec..." << std::endl;
    sleep(2.0);
    readBuffer = "";
    curl_easy_setopt(params.curl, CURLOPT_DNS_CACHE_TIMEOUT, 0);
    resCurl = curl_easy_perform(params.curl);
  }
  json_t* root;
  json_error_t error;
  root = json_loads(readBuffer.c_str(), 0, &error);
  while (!root) {
    *params.logFile << "Error with JSON:\n" << error.text << std::endl;
    *params.logFile << "Buffer:\n" << readBuffer.c_str() << std::endl;
    *params.logFile << "Retrying..." << std::endl;
    sleep(2.0);
    readBuffer = "";
    resCurl = curl_easy_perform(params.curl);
    while (resCurl != CURLE_OK) {
      *params.logFile << "Error with cURL: " << curl_easy_strerror(resCurl) << std::endl;
      *params.logFile << "  URL: " << url << std::endl;
      *params.logFile << "  Retry in 2 sec..." << std::endl;
      sleep(2.0);
      readBuffer = "";
      resCurl = curl_easy_perform(params.curl);
    }
    root = json_loads(readBuffer.c_str(), 0, &error);
  }
  // Change back to POST request
  if (getRequest) {
    curl_easy_setopt(params.curl, CURLOPT_CUSTOMREQUEST, "POST");
  }
  return root;
}
<commit_msg>Refactored 'getJsonFromUrl' into state machine. Code cleanup.<commit_after>#include "curl_fun.h"
#include "parameters.h"

#include "curl/curl.h"
#include "jansson.h"

#include <unistd.h>

size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp)
{
  ((std::string*)userp)->append((char*)contents, size * nmemb);
  return size * nmemb;
}

json_t* getJsonFromUrl(Parameters& params, std::string url, std::string postFields,
                       bool getRequest)
{
  if (!params.cacert.empty())
    curl_easy_setopt(params.curl, CURLOPT_CAINFO, params.cacert.c_str());

  curl_easy_setopt(params.curl, CURLOPT_URL, url.c_str());
  curl_easy_setopt(params.curl, CURLOPT_CONNECTTIMEOUT, 10L);
  curl_easy_setopt(params.curl, CURLOPT_TIMEOUT, 20L);
  curl_easy_setopt(params.curl, CURLOPT_ENCODING, "gzip");
  if (!postFields.empty())
    curl_easy_setopt(params.curl, CURLOPT_POSTFIELDS, postFields.c_str());

  curl_easy_setopt(params.curl, CURLOPT_WRITEFUNCTION, WriteCallback);
  std::string readBuffer;
  curl_easy_setopt(params.curl, CURLOPT_WRITEDATA, &readBuffer);
  curl_easy_setopt(params.curl, CURLOPT_DNS_CACHE_TIMEOUT, 3600);
  // Some calls must be GET requests
  if (getRequest)
    curl_easy_setopt(params.curl, CURLOPT_CUSTOMREQUEST, "GET");

  goto curl_state;

retry_state:
  sleep(2.0);
  readBuffer = "";
  curl_easy_setopt(params.curl, CURLOPT_DNS_CACHE_TIMEOUT, 0);

curl_state:
  CURLcode resCurl = curl_easy_perform(params.curl);
  if (resCurl != CURLE_OK)
  {
    *params.logFile << "Error with cURL: " << curl_easy_strerror(resCurl) << '\n'
                    << "  URL: " << url << '\n'
                    << "  Retry in 2 sec..." << std::endl;

    goto retry_state;
  }

/* documentation label */
// json_state:
  json_error_t error;
  json_t *root = json_loads(readBuffer.c_str(), 0, &error);
  if (!root)
  {
    *params.logFile << "Error with JSON:\n" << error.text << '\n'
                    << "Buffer:\n" << readBuffer << '\n'
                    << "Retrying..." << std::endl;

    goto retry_state;
  }

  // Change back to POST request
  if (getRequest)
    curl_easy_setopt(params.curl, CURLOPT_CUSTOMREQUEST, "POST");

  return root;
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 sails Authors.
// All rights reserved.
//
// Official git repository and contact information can be found at
// https://github.com/sails/sails and http://www.sailsxu.com/.
//
// Filename: rank_service.cc
//
// Author: sailsxu <sailsxu@gmail.com>
// Created: 2015-04-27 16:36:55

#include "rank_service.h"

namespace sails {

const char* rank_day = "fight_rank_day";
const char* rank_week = "fight_rank_week";
const char* rank_month = "fight_rank_month";

RankServiceImp::RankServiceImp() : key("safrankservicecontroller") {
  c = NULL;
}

RankServiceImp::~RankServiceImp() {
  if (c) {
    redisFree(c);
  }
}

// 连接redis
void RankServiceImp::connect() {
  const char *hostname = "127.0.0.1";
  int port = 6379;
  struct timeval timeout = { 1, 500000 };  // 1.5 seconds
  c = redisConnectWithTimeout(hostname, port, timeout);
  if (c == NULL || c->err) {
    if (c) {
      printf("Connection error: %s\n", c->errstr);
      redisFree(c);
    } else {
      printf("Connection error: can't allocate redis context\n");
    }
    exit(1);
  }
}

// 得到排行榜前面的用户
void RankServiceImp::GetRanklist(::google::protobuf::RpcController*,
                                 const ::sails::RanklistRequest* request,
                                 ::sails::RanklistResponse* response,
                                 ::google::protobuf::Closure*) {
  int topnum = request->top();
  if (topnum <= 0) {
    topnum = 1;
  }
  topnum = topnum > 50 ? 50:topnum;
  char keyname[20] = {'\0'};
  int type = 0;
  if (request->type() == TimeType::DAY) {
    snprintf(keyname, sizeof(keyname), "%s", rank_day);
    type = 1;
  } else if (request->type() == TimeType::WEEK) {
    snprintf(keyname, sizeof(keyname), "%s", rank_week);
    type = 2;
  } else if (request->type() == TimeType::MONTH) {
    snprintf(keyname, sizeof(keyname), "%s", rank_month);
    type = 3;
  }
  redisReply* reply = reinterpret_cast<redisReply*>(
      redisCommand(c, "ZREVRANGE %s 0 %d", keyname, topnum-1));
  if (reply->type == REDIS_REPLY_ARRAY) {
    uint32_t num = reply->elements;
    for (size_t j = 0; j < num; j++) {
      RanklistResponse::RankItem* item = response->add_ranklist();
      item->set_rank(j);
      item->set_accountid(reply->element[j]->str);
      // 分数
      item->set_score(getuserscore(type, reply->element[j]->str));
    }
  }
  handleException(reply);
  freeReplyObject(reply);
  response->set_err_code(sails::ERR_CODE::SUCCESS);
}

// 得到自己的排行信息
void RankServiceImp::GetUserScore(::google::protobuf::RpcController*,
                                  const ::sails::RankScoreRequest* request,
                                  ::sails::RankScoreResponse* response,
                                  ::google::protobuf::Closure*) {
  int type = 0;
  if (request->type() == TimeType::DAY) {
    type = 1;
  } else if (request->type() == TimeType::WEEK) {
    type = 2;
  } else if (request->type() == TimeType::MONTH) {
    type = 3;
  }
  // 分数
  response->set_score(getuserscore(type, request->accountid().c_str()));
  // 排行
  response->set_rank(getuserrank(type, request->accountid().c_str()));
  response->set_err_code(sails::ERR_CODE::SUCCESS);
}

// 得到自己的对战次数
void RankServiceImp::GetUserFightTimes(
    ::google::protobuf::RpcController*,
    const ::sails::RankFightTimesRequest* request,
    ::sails::RankFightTimesResponse* response,
    ::google::protobuf::Closure*) {
  bool result = true;
  redisReply* reply = reinterpret_cast<redisReply*>(
      redisCommand(c, "get user_fight_failed_%s",
                   request->accountid().c_str()));
  int failedTimes = 0;
  if (reply->type == REDIS_REPLY_STRING) {
    sscanf(reply->str, "%10d", &failedTimes);
  } else {
    result = false;
  }
  handleException(reply);
  freeReplyObject(reply);
  reply = reinterpret_cast<redisReply*>(
      redisCommand(c, "get user_fight_win_%s", request->accountid().c_str()));
  int winTimes = 0;
  if (reply->type == REDIS_REPLY_STRING) {
    sscanf(reply->str, "%10d", &winTimes);
  } else {
    result = false;
  }
  handleException(reply);
  freeReplyObject(reply);
  reply = reinterpret_cast<redisReply*>(
      redisCommand(c, "get user_fight_escape_%s",
                   request->accountid().c_str()));
  int escapeTimes = 0;
  if (reply->type == REDIS_REPLY_STRING) {
    sscanf(reply->str, "%10d", &escapeTimes);
  } else {
    result = false;
  }
  handleException(reply);
  freeReplyObject(reply);

  response->set_wintimes(winTimes);
  response->set_failedtimes(failedTimes);
  response->set_escapetimes(escapeTimes);
  response->set_tietimes(0);  // 没有平
  if (result) {
    response->set_err_code(::sails::ERR_CODE::SUCCESS);
  } else {
    response->set_err_code(::sails::ERR_CODE::ERR);
  }
}

// 得到对战数据，返回列表头部元素
void RankServiceImp::GetFightRecordData(
    ::google::protobuf::RpcController*,
    const ::sails::RankFightRecordDataRequest*,
    ::sails::RankFightRecordDataResponse* response,
    ::google::protobuf::Closure*) {
  char data[100] = {'\0'};
  if (getNextFightRecord(data, sizeof(data))) {
    response->set_data(data);
    response->set_err_code(sails::ERR_CODE::SUCCESS);
  } else {
    response->set_err_code(sails::ERR_CODE::ERR);
  }
}
// 删除对战数据
void RankServiceImp::DeleteFightRecordData(
    ::google::protobuf::RpcController*,
    const ::sails::RankFightRecordDataDeleteRequest* request,
    ::sails::RankFightRecordDataDeleteResponse* response,
    ::google::protobuf::Closure*) {
  if (request->key() == key) {
    if (deleteFightRecord(request->data().c_str())) {
      response->set_err_code(sails::ERR_CODE::SUCCESS);
    } else {
      response->set_err_code(sails::ERR_CODE::ERR);
    }
  } else {
    response->set_err_code(sails::ERR_CODE::KEY_INVALID);
  }
}

// 增加对战结果
void RankServiceImp::AddFightResult(
    ::google::protobuf::RpcController*,
    const ::sails::RankAddFightResultRequest* request,
    ::sails::RankAddFightResultResponse* response,
    ::google::protobuf::Closure*) {
  if (request->key() != key) {
    response->set_err_code(sails::ERR_CODE::KEY_INVALID);
  } else {
    int score = 0;
    if (request->result() == sails::RankAddFightResultRequest::WIN) {
      score = 3;
    } else if (request->result() == sails::RankAddFightResultRequest::FAILED) {
      score = 2;
    } else if (request->result() == sails::RankAddFightResultRequest::ESCAPE) {
      score = -1;
    }
    // 增加同步消息
    char record[100] = {'\0'};
    snprintf(record, sizeof(record), "%s|%d|%d|%d|%s|%d|%d",
             request->accountid().c_str(), request->gameid(), request->roomid(),
             request->roomtype(), request->overtime().c_str(),
             request->result(), score);
    if (addFightRecord(record)) {
      // 增加胜负次数
      adduserfighttimes(request->accountid().c_str(), request->result());
      printf("add user score\n");
      // 增加分数
      adduserscore(request->accountid().c_str(), score);
      response->set_err_code(sails::ERR_CODE::SUCCESS);
    } else {
      response->set_err_code(sails::ERR_CODE::ERR);
    }
  }
}

void RankServiceImp::DeleteRanklist(
    ::google::protobuf::RpcController*,
    const ::sails::DeleteRanklistRequest* request,
    ::sails::DeleteRanklistResponse* response,
    ::google::protobuf::Closure*) {
  if (request->key() == key) {
    char rediskey[100] = {'\0'};
    if (request->type() == TimeType::DAY) {
      snprintf(rediskey, sizeof(rediskey), "%s", rank_day);
    } else if (request->type() == TimeType::WEEK) {
      snprintf(rediskey, sizeof(rediskey), "%s", rank_week);
    } else if (request->type() == TimeType::MONTH) {
      snprintf(rediskey, sizeof(rediskey), "%s", rank_month);
    }
    redisReply* reply = reinterpret_cast<redisReply*>(
        redisCommand(c, "ZREMRANGEBYRANK %s 0 10000000", rediskey));
    if (reply->type != REDIS_REPLY_ERROR) {
      response->set_err_code(sails::ERR_CODE::SUCCESS);
    } else {
      response->set_err_code(sails::ERR_CODE::ERR);
    }
  } else {
    response->set_err_code(sails::ERR_CODE::KEY_INVALID);
  }
}

// 得到用户分数
int RankServiceImp::getuserscore(int type,
                                 const char* accountId) {
  char keyname[30] = {'\0'};
  if (type == 1) {
    snprintf(keyname, sizeof(keyname), "%s", rank_day);
  } else if (type == 2) {
    snprintf(keyname, sizeof(keyname), "%s", rank_week);
  } else if (type == 3) {
    snprintf(keyname, sizeof(keyname), "%s", rank_month);
  } else {
    return 0;
  }
  redisReply* reply =
      reinterpret_cast<redisReply*>(
          redisCommand(c, "ZSCORE %s %s", keyname, accountId));
  int score = 0;
  if (reply->type == REDIS_REPLY_STRING) {
    sscanf(reply->str, "%10d", &score);
  }
  handleException(reply);
  freeReplyObject(reply);
  return score;
}

// 得到用户排行
int RankServiceImp::getuserrank(int type, const char* accountId) {
  char keyname[30] = {'\0'};
  if (type == 1) {
    snprintf(keyname, sizeof(keyname), "%s", rank_day);
  } else if (type == 2) {
    snprintf(keyname, sizeof(keyname), "%s", rank_week);
  } else if (type == 3) {
    snprintf(keyname, sizeof(keyname), "%s", rank_month);
  } else {
    return 0;
  }
  redisReply* reply =
      reinterpret_cast<redisReply*>(redisCommand(
          c, "ZREVRANK %s %s", keyname, accountId));
  int rank = 0;
  if (reply->type == REDIS_REPLY_INTEGER) {
    rank = reply->integer;
  }
  handleException(reply);
  freeReplyObject(reply);
  return rank;
}

// 增加用户分数,三个排行榜都要增加
int RankServiceImp::adduserscore(const char* accountId, int score) {
  int lastscore = 0;
  redisReply* reply = reinterpret_cast<redisReply*>(
      redisCommand(c, "ZINCRBY %s %d %s", rank_day, score, accountId));
  if (reply->type == REDIS_REPLY_STRING) {
    sscanf(reply->str, "%10d", &lastscore);
  }
  printf("add user score lastscore:%d\n", lastscore);
  handleException(reply);
  freeReplyObject(reply);
  reply = reinterpret_cast<redisReply*>(
      redisCommand(c, "ZINCRBY %s %d %s", rank_week, score, accountId));
  handleException(reply);
  freeReplyObject(reply);
  reply = reinterpret_cast<redisReply*>(
      redisCommand(c, "ZINCRBY %s %d %s", rank_month, score, accountId));
  handleException(reply);
  freeReplyObject(reply);

  return lastscore;
}

// 增加用户胜负次数
// isWin，-1负；1胜；-2逃跑
int RankServiceImp::adduserfighttimes(
    const char* accountId, RankAddFightResultRequest::Result result) {
  char key[200] = {'\0'};
  if (result == RankAddFightResultRequest::FAILED) {
    snprintf(key, sizeof(key), "user_fight_failed_%s", accountId);
  } else if (result == RankAddFightResultRequest::WIN) {
    snprintf(key, sizeof(key), "user_fight_win_%s", accountId);
  } else if (result == RankAddFightResultRequest::ESCAPE) {
    snprintf(key, sizeof(key), "user_fight_escape_%s", accountId);
  }

  int times = 0;
  redisReply* reply = reinterpret_cast<redisReply*>(
      redisCommand(c, "INCR %s", key));
  if (reply->type == REDIS_REPLY_INTEGER) {
    times = reply->integer;
  }
  handleException(reply);
  freeReplyObject(reply);
  return times;
}

// 增加对战记录，用于通知后台
bool RankServiceImp::addFightRecord(const char* record) {
  bool result = false;
  redisReply* reply =
      reinterpret_cast<redisReply*>(redisCommand(
          c, "rpush fightrecord %s", record));
  if (reply->type != REDIS_REPLY_ERROR) {
    result = true;
  }
  handleException(reply);
  freeReplyObject(reply);
  return result;
}

// 得到下一个记录
bool RankServiceImp::getNextFightRecord(char* data, int len) {
  bool result = false;
  redisReply* reply =
      reinterpret_cast<redisReply*>(redisCommand(
          c, "LINDEX fightrecord 0"));
  if (reply->type != REDIS_REPLY_ERROR) {
    if (reply->type == REDIS_REPLY_STRING) {
      snprintf(data, len, "%s", reply->str);
    }
    result = true;
  }
  handleException(reply);
  freeReplyObject(reply);
  return result;
}

// 删除记录
bool RankServiceImp::deleteFightRecord(const char *record) {
  bool result = false;
  redisReply* reply =
      reinterpret_cast<redisReply*>(redisCommand(
          c, "LREM fightrecord 1 %s", record));
  if (reply->type != REDIS_REPLY_ERROR) {
    result = true;
  }
  handleException(reply);
  freeReplyObject(reply);
  return result;
}

void RankServiceImp::handleException(redisReply* reply) {
  if (reply->type == REDIS_REPLY_ERROR) {
    // 重连
    connect();
  }
}

}  // namespace sails
<commit_msg>update<commit_after>// Copyright (C) 2015 sails Authors.
// All rights reserved.
//
// Official git repository and contact information can be found at
// https://github.com/sails/sails and http://www.sailsxu.com/.
//
// Filename: rank_service.cc
//
// Author: sailsxu <sailsxu@gmail.com>
// Created: 2015-04-27 16:36:55

#include "rank_service.h"

namespace sails {

const char* rank_day = "fight_rank_day";
const char* rank_week = "fight_rank_week";
const char* rank_month = "fight_rank_month";

RankServiceImp::RankServiceImp() : key("safrankservicecontroller") {
  c = NULL;
}

RankServiceImp::~RankServiceImp() {
  if (c) {
    redisFree(c);
  }
}

// 连接redis
void RankServiceImp::connect() {
  const char *hostname = "127.0.0.1";
  int port = 6379;
  struct timeval timeout = { 1, 500000 };  // 1.5 seconds
  c = redisConnectWithTimeout(hostname, port, timeout);
  if (c == NULL || c->err) {
    if (c) {
      printf("Connection error: %s\n", c->errstr);
      redisFree(c);
    } else {
      printf("Connection error: can't allocate redis context\n");
    }
    exit(1);
  }
}

// 得到排行榜前面的用户
void RankServiceImp::GetRanklist(::google::protobuf::RpcController*,
                                 const ::sails::RanklistRequest* request,
                                 ::sails::RanklistResponse* response,
                                 ::google::protobuf::Closure*) {
  int topnum = request->top();
  if (topnum <= 0) {
    topnum = 1;
  }
  topnum = topnum > 50 ? 50:topnum;
  char keyname[20] = {'\0'};
  int type = 0;
  if (request->type() == TimeType::DAY) {
    snprintf(keyname, sizeof(keyname), "%s", rank_day);
    type = 1;
  } else if (request->type() == TimeType::WEEK) {
    snprintf(keyname, sizeof(keyname), "%s", rank_week);
    type = 2;
  } else if (request->type() == TimeType::MONTH) {
    snprintf(keyname, sizeof(keyname), "%s", rank_month);
    type = 3;
  }
  redisReply* reply = reinterpret_cast<redisReply*>(
      redisCommand(c, "ZREVRANGE %s 0 %d", keyname, topnum-1));
  if (reply->type == REDIS_REPLY_ARRAY) {
    uint32_t num = reply->elements;
    for (size_t j = 0; j < num; j++) {
      RanklistResponse::RankItem* item = response->add_ranklist();
      item->set_rank(j);
      item->set_accountid(reply->element[j]->str);
      // 分数
      item->set_score(getuserscore(type, reply->element[j]->str));
    }
  }
  handleException(reply);
  freeReplyObject(reply);
  response->set_err_code(sails::ERR_CODE::SUCCESS);
}

// 得到自己的排行信息
void RankServiceImp::GetUserScore(::google::protobuf::RpcController*,
                                  const ::sails::RankScoreRequest* request,
                                  ::sails::RankScoreResponse* response,
                                  ::google::protobuf::Closure*) {
  int type = 0;
  if (request->type() == TimeType::DAY) {
    type = 1;
  } else if (request->type() == TimeType::WEEK) {
    type = 2;
  } else if (request->type() == TimeType::MONTH) {
    type = 3;
  }
  // 分数
  response->set_score(getuserscore(type, request->accountid().c_str()));
  // 排行
  response->set_rank(getuserrank(type, request->accountid().c_str()));
  response->set_err_code(sails::ERR_CODE::SUCCESS);
}

// 得到自己的对战次数
void RankServiceImp::GetUserFightTimes(
    ::google::protobuf::RpcController*,
    const ::sails::RankFightTimesRequest* request,
    ::sails::RankFightTimesResponse* response,
    ::google::protobuf::Closure*) {
  bool result = true;
  redisReply* reply = reinterpret_cast<redisReply*>(
      redisCommand(c, "get user_fight_failed_%s",
                   request->accountid().c_str()));
  int failedTimes = 0;
  if (reply->type == REDIS_REPLY_STRING) {
    sscanf(reply->str, "%10d", &failedTimes);
  } else if (reply->type == REDIS_REPLY_NIL) {
    failedTimes = 0;
  } else {
    result = false;
  }
  handleException(reply);
  freeReplyObject(reply);
  reply = reinterpret_cast<redisReply*>(
      redisCommand(c, "get user_fight_win_%s", request->accountid().c_str()));
  int winTimes = 0;
  if (reply->type == REDIS_REPLY_STRING) {
    sscanf(reply->str, "%10d", &winTimes);
  } else if (reply->type == REDIS_REPLY_NIL) {
    winTimes = 0;
  } else {
    result = false;
  }
  handleException(reply);
  freeReplyObject(reply);
  reply = reinterpret_cast<redisReply*>(
      redisCommand(c, "get user_fight_escape_%s",
                   request->accountid().c_str()));
  int escapeTimes = 0;
  if (reply->type == REDIS_REPLY_STRING) {
    sscanf(reply->str, "%10d", &escapeTimes);
  } else if (reply->type == REDIS_REPLY_NIL) {
    escapeTimes = 0;
  } else {
    result = false;
  }
  handleException(reply);
  freeReplyObject(reply);

  response->set_wintimes(winTimes);
  response->set_failedtimes(failedTimes);
  response->set_escapetimes(escapeTimes);
  response->set_tietimes(0);  // 没有平
  if (result) {
    response->set_err_code(::sails::ERR_CODE::SUCCESS);
  } else {
    response->set_err_code(::sails::ERR_CODE::ERR);
  }
}

// 得到对战数据，返回列表头部元素
void RankServiceImp::GetFightRecordData(
    ::google::protobuf::RpcController*,
    const ::sails::RankFightRecordDataRequest*,
    ::sails::RankFightRecordDataResponse* response,
    ::google::protobuf::Closure*) {
  char data[100] = {'\0'};
  if (getNextFightRecord(data, sizeof(data))) {
    response->set_data(data);
    response->set_err_code(sails::ERR_CODE::SUCCESS);
  } else {
    response->set_err_code(sails::ERR_CODE::ERR);
  }
}
// 删除对战数据
void RankServiceImp::DeleteFightRecordData(
    ::google::protobuf::RpcController*,
    const ::sails::RankFightRecordDataDeleteRequest* request,
    ::sails::RankFightRecordDataDeleteResponse* response,
    ::google::protobuf::Closure*) {
  if (request->key() == key) {
    if (deleteFightRecord(request->data().c_str())) {
      response->set_err_code(sails::ERR_CODE::SUCCESS);
    } else {
      response->set_err_code(sails::ERR_CODE::ERR);
    }
  } else {
    response->set_err_code(sails::ERR_CODE::KEY_INVALID);
  }
}

// 增加对战结果
void RankServiceImp::AddFightResult(
    ::google::protobuf::RpcController*,
    const ::sails::RankAddFightResultRequest* request,
    ::sails::RankAddFightResultResponse* response,
    ::google::protobuf::Closure*) {
  if (request->key() != key) {
    response->set_err_code(sails::ERR_CODE::KEY_INVALID);
  } else {
    int score = 0;
    if (request->result() == sails::RankAddFightResultRequest::WIN) {
      score = 3;
    } else if (request->result() == sails::RankAddFightResultRequest::FAILED) {
      score = 2;
    } else if (request->result() == sails::RankAddFightResultRequest::ESCAPE) {
      score = -1;
    }
    // 增加同步消息
    char record[100] = {'\0'};
    snprintf(record, sizeof(record), "%s|%d|%d|%d|%s|%d|%d",
             request->accountid().c_str(), request->gameid(), request->roomid(),
             request->roomtype(), request->overtime().c_str(),
             request->result(), score);
    if (addFightRecord(record)) {
      // 增加胜负次数
      adduserfighttimes(request->accountid().c_str(), request->result());
      printf("add user score\n");
      // 增加分数
      adduserscore(request->accountid().c_str(), score);
      response->set_err_code(sails::ERR_CODE::SUCCESS);
    } else {
      response->set_err_code(sails::ERR_CODE::ERR);
    }
  }
}

void RankServiceImp::DeleteRanklist(
    ::google::protobuf::RpcController*,
    const ::sails::DeleteRanklistRequest* request,
    ::sails::DeleteRanklistResponse* response,
    ::google::protobuf::Closure*) {
  if (request->key() == key) {
    char rediskey[100] = {'\0'};
    if (request->type() == TimeType::DAY) {
      snprintf(rediskey, sizeof(rediskey), "%s", rank_day);
    } else if (request->type() == TimeType::WEEK) {
      snprintf(rediskey, sizeof(rediskey), "%s", rank_week);
    } else if (request->type() == TimeType::MONTH) {
      snprintf(rediskey, sizeof(rediskey), "%s", rank_month);
    }
    redisReply* reply = reinterpret_cast<redisReply*>(
        redisCommand(c, "ZREMRANGEBYRANK %s 0 10000000", rediskey));
    if (reply->type != REDIS_REPLY_ERROR) {
      response->set_err_code(sails::ERR_CODE::SUCCESS);
    } else {
      response->set_err_code(sails::ERR_CODE::ERR);
    }
  } else {
    response->set_err_code(sails::ERR_CODE::KEY_INVALID);
  }
}

// 得到用户分数
int RankServiceImp::getuserscore(int type,
                                 const char* accountId) {
  char keyname[30] = {'\0'};
  if (type == 1) {
    snprintf(keyname, sizeof(keyname), "%s", rank_day);
  } else if (type == 2) {
    snprintf(keyname, sizeof(keyname), "%s", rank_week);
  } else if (type == 3) {
    snprintf(keyname, sizeof(keyname), "%s", rank_month);
  } else {
    return 0;
  }
  redisReply* reply =
      reinterpret_cast<redisReply*>(
          redisCommand(c, "ZSCORE %s %s", keyname, accountId));
  int score = 0;
  if (reply->type == REDIS_REPLY_STRING) {
    sscanf(reply->str, "%10d", &score);
  }
  handleException(reply);
  freeReplyObject(reply);
  return score;
}

// 得到用户排行
int RankServiceImp::getuserrank(int type, const char* accountId) {
  char keyname[30] = {'\0'};
  if (type == 1) {
    snprintf(keyname, sizeof(keyname), "%s", rank_day);
  } else if (type == 2) {
    snprintf(keyname, sizeof(keyname), "%s", rank_week);
  } else if (type == 3) {
    snprintf(keyname, sizeof(keyname), "%s", rank_month);
  } else {
    return 0;
  }
  redisReply* reply =
      reinterpret_cast<redisReply*>(redisCommand(
          c, "ZREVRANK %s %s", keyname, accountId));
  int rank = 0;
  if (reply->type == REDIS_REPLY_INTEGER) {
    rank = reply->integer;
  }
  handleException(reply);
  freeReplyObject(reply);
  return rank;
}

// 增加用户分数,三个排行榜都要增加
int RankServiceImp::adduserscore(const char* accountId, int score) {
  int lastscore = 0;
  redisReply* reply = reinterpret_cast<redisReply*>(
      redisCommand(c, "ZINCRBY %s %d %s", rank_day, score, accountId));
  if (reply->type == REDIS_REPLY_STRING) {
    sscanf(reply->str, "%10d", &lastscore);
  }
  printf("add user score lastscore:%d\n", lastscore);
  handleException(reply);
  freeReplyObject(reply);
  reply = reinterpret_cast<redisReply*>(
      redisCommand(c, "ZINCRBY %s %d %s", rank_week, score, accountId));
  handleException(reply);
  freeReplyObject(reply);
  reply = reinterpret_cast<redisReply*>(
      redisCommand(c, "ZINCRBY %s %d %s", rank_month, score, accountId));
  handleException(reply);
  freeReplyObject(reply);

  return lastscore;
}

// 增加用户胜负次数
// isWin，-1负；1胜；-2逃跑
int RankServiceImp::adduserfighttimes(
    const char* accountId, RankAddFightResultRequest::Result result) {
  char key[200] = {'\0'};
  if (result == RankAddFightResultRequest::FAILED) {
    snprintf(key, sizeof(key), "user_fight_failed_%s", accountId);
  } else if (result == RankAddFightResultRequest::WIN) {
    snprintf(key, sizeof(key), "user_fight_win_%s", accountId);
  } else if (result == RankAddFightResultRequest::ESCAPE) {
    snprintf(key, sizeof(key), "user_fight_escape_%s", accountId);
  }

  int times = 0;
  redisReply* reply = reinterpret_cast<redisReply*>(
      redisCommand(c, "INCR %s", key));
  if (reply->type == REDIS_REPLY_INTEGER) {
    times = reply->integer;
  }
  handleException(reply);
  freeReplyObject(reply);
  return times;
}

// 增加对战记录，用于通知后台
bool RankServiceImp::addFightRecord(const char* record) {
  bool result = false;
  redisReply* reply =
      reinterpret_cast<redisReply*>(redisCommand(
          c, "rpush fightrecord %s", record));
  if (reply->type != REDIS_REPLY_ERROR) {
    result = true;
  }
  handleException(reply);
  freeReplyObject(reply);
  return result;
}

// 得到下一个记录
bool RankServiceImp::getNextFightRecord(char* data, int len) {
  bool result = false;
  redisReply* reply =
      reinterpret_cast<redisReply*>(redisCommand(
          c, "LINDEX fightrecord 0"));
  if (reply->type != REDIS_REPLY_ERROR) {
    if (reply->type == REDIS_REPLY_STRING) {
      snprintf(data, len, "%s", reply->str);
    }
    result = true;
  }
  handleException(reply);
  freeReplyObject(reply);
  return result;
}

// 删除记录
bool RankServiceImp::deleteFightRecord(const char *record) {
  bool result = false;
  redisReply* reply =
      reinterpret_cast<redisReply*>(redisCommand(
          c, "LREM fightrecord 1 %s", record));
  if (reply->type != REDIS_REPLY_ERROR) {
    result = true;
  }
  handleException(reply);
  freeReplyObject(reply);
  return result;
}

void RankServiceImp::handleException(redisReply* reply) {
  if (reply->type == REDIS_REPLY_ERROR) {
    // 重连
    connect();
  }
}

}  // namespace sails
<|endoftext|>


<commit_before>
#include <libclientserver.h>

/**
 * Hex2Str
 * @param[in] str the string value to be decoded
 * @param[out] out the string which was decoded
 * @return True for success and False for failure
 *
 * HexDecode decode a string of hex and returns the same decoded value
 * An example would be "20" will be returned as " "
 */
bool Decoder::Hex2Str(const std::string &str, std::string &out) {
	const char *c = str.c_str();
	unsigned int x;

	out = "";

	while(*c != 0) {
		//FIXME: fail on poor data
		sscanf(c, "%2X", &x);
		out += x;
		c += 2;
	}

	return true;
}

bool Decoder::Str2Buf(const std::string &str, char **buf, size_t *buflen)
{
	abort();
	return false;
}

bool Decoder::Str2Char(const std::string &str, char value)
{
	abort();
	return false;
}

bool Decoder::Str2UChar(const std::string &str, unsigned char value)
{
	abort();
	return false;
}

bool Decoder::Str2Short(const std::string &str, short value)
{
	abort();
	return false;
}

bool Decoder::Str2UShort(const std::string &str, unsigned short value)
{
	abort();
	return false;
}

bool Decoder::Str2Int(const std::string &str, int value)
{
	abort();
	return false;
}

bool Decoder::Str2UInt(const std::string &str, unsigned int value)
{
	abort();
	return false;
}

bool Str2Long(const std::string &str, long value)
{
	abort();
	return false;
}

bool Str2ULong(const std::string &str, unsigned long value)
{
	abort();
	return false;
}

bool Str2Float(const std::string &str, float value)
{
	abort();
	return false;
}

bool Str2Double(const std::string &str, double value)
{
	abort();
	return false;
}

<commit_msg>Added Check to Hex2Str<commit_after>
#include <libclientserver.h>

/**
 * Hex2Str
 * @param[in] str the string value to be decoded
 * @param[out] out the string which was decoded
 * @return True for success and False for failure
 *
 * HexDecode decode a string of hex and returns the same decoded value
 * An example would be "20" will be returned as " "
 */
bool Decoder::Hex2Str(const std::string &str, std::string &out) {
	const char *c = str.c_str();
	unsigned int x;

	//Must be an even length
	if (str.size() % 2 != 0)
		return false;

	out = "";

	while(c[0] != 0) {
		sscanf(c, "%2X", &x);
		out += x;
		c += 2;
	}

	return true;
}

bool Decoder::Str2Buf(const std::string &str, char **buf, size_t *buflen)
{
	abort();
	return false;
}

bool Decoder::Str2Char(const std::string &str, char value)
{
	abort();
	return false;
}

bool Decoder::Str2UChar(const std::string &str, unsigned char value)
{
	abort();
	return false;
}

bool Decoder::Str2Short(const std::string &str, short value)
{
	abort();
	return false;
}

bool Decoder::Str2UShort(const std::string &str, unsigned short value)
{
	abort();
	return false;
}

bool Decoder::Str2Int(const std::string &str, int value)
{
	abort();
	return false;
}

bool Decoder::Str2UInt(const std::string &str, unsigned int value)
{
	abort();
	return false;
}

bool Str2Long(const std::string &str, long value)
{
	abort();
	return false;
}

bool Str2ULong(const std::string &str, unsigned long value)
{
	abort();
	return false;
}

bool Str2Float(const std::string &str, float value)
{
	abort();
	return false;
}

bool Str2Double(const std::string &str, double value)
{
	abort();
	return false;
}

<|endoftext|>