<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/basictypes.h"
#include "base/gfx/png_encoder.h"
#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "third_party/skia/include/core/SkBitmap.h"

extern "C" {
#include "third_party/libpng/png.h"
}

namespace {

// Converts BGRA->RGBA and RGBA->BGRA.
void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width,
                               unsigned char* output) {
  for (int x = 0; x < pixel_width; x++) {
    const unsigned char* pixel_in = &input[x * 4];
    unsigned char* pixel_out = &output[x * 4];
    pixel_out[0] = pixel_in[2];
    pixel_out[1] = pixel_in[1];
    pixel_out[2] = pixel_in[0];
    pixel_out[3] = pixel_in[3];
  }
}

void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width,
                      unsigned char* rgb) {
  for (int x = 0; x < pixel_width; x++) {
    const unsigned char* pixel_in = &rgba[x * 4];
    unsigned char* pixel_out = &rgb[x * 3];
    pixel_out[0] = pixel_in[0];
    pixel_out[1] = pixel_in[1];
    pixel_out[2] = pixel_in[2];
  }
}

}  // namespace

// Encoder --------------------------------------------------------------------
//
// This section of the code is based on nsPNGEncoder.cpp in Mozilla
// (Copyright 2005 Google Inc.)

namespace {

// Passed around as the io_ptr in the png structs so our callbacks know where
// to write data.
struct PngEncoderState {
  PngEncoderState(std::vector<unsigned char>* o) : out(o) {}
  std::vector<unsigned char>* out;
};

// Called by libpng to flush its internal buffer to ours.
void EncoderWriteCallback(png_structp png, png_bytep data, png_size_t size) {
  PngEncoderState* state = static_cast<PngEncoderState*>(png_get_io_ptr(png));
  DCHECK(state->out);

  size_t old_size = state->out->size();
  state->out->resize(old_size + size);
  memcpy(&(*state->out)[old_size], data, size);
}

void ConvertBGRAtoRGB(const unsigned char* bgra, int pixel_width,
                      unsigned char* rgb) {
  for (int x = 0; x < pixel_width; x++) {
    const unsigned char* pixel_in = &bgra[x * 4];
    unsigned char* pixel_out = &rgb[x * 3];
    pixel_out[0] = pixel_in[2];
    pixel_out[1] = pixel_in[1];
    pixel_out[2] = pixel_in[0];
  }
}

// Automatically destroys the given write structs on destruction to make
// cleanup and error handling code cleaner.
class PngWriteStructDestroyer {
 public:
  PngWriteStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {
  }
  ~PngWriteStructDestroyer() {
    png_destroy_write_struct(ps_, pi_);
  }
 private:
  png_struct** ps_;
  png_info** pi_;

  DISALLOW_EVIL_CONSTRUCTORS(PngWriteStructDestroyer);
};

}  // namespace

// static
bool PNGEncoder::Encode(const unsigned char* input, ColorFormat format,
                      int w, int h, int row_byte_width,
                      bool discard_transparency,
                      std::vector<unsigned char>* output) {
  // Run to convert an input row into the output row format, NULL means no
  // conversion is necessary.
  void (*converter)(const unsigned char* in, int w, unsigned char* out) = NULL;

  int input_color_components, output_color_components;
  int png_output_color_type;
  switch (format) {
    case FORMAT_RGB:
      input_color_components = 3;
      output_color_components = 3;
      png_output_color_type = PNG_COLOR_TYPE_RGB;
      discard_transparency = false;
      break;

    case FORMAT_RGBA:
      input_color_components = 4;
      if (discard_transparency) {
        output_color_components = 3;
        png_output_color_type = PNG_COLOR_TYPE_RGB;
        converter = ConvertRGBAtoRGB;
      } else {
        output_color_components = 4;
        png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
        converter = NULL;
      }
      break;

    case FORMAT_BGRA:
      input_color_components = 4;
      if (discard_transparency) {
        output_color_components = 3;
        png_output_color_type = PNG_COLOR_TYPE_RGB;
        converter = ConvertBGRAtoRGB;
      } else {
        output_color_components = 4;
        png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
        converter = ConvertBetweenBGRAandRGBA;
      }
      break;

    default:
      NOTREACHED() << "Unknown pixel format";
      return false;
  }

  // Row stride should be at least as long as the length of the data.
  DCHECK(input_color_components * w <= row_byte_width);

  png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
                                                png_voidp_NULL,
                                                png_error_ptr_NULL,
                                                png_error_ptr_NULL);
  if (!png_ptr)
    return false;
  png_info* info_ptr = png_create_info_struct(png_ptr);
  if (!info_ptr) {
    png_destroy_write_struct(&png_ptr, NULL);
    return false;
  }
  PngWriteStructDestroyer destroyer(&png_ptr, &info_ptr);

  if (setjmp(png_jmpbuf(png_ptr))) {
    // The destroyer will ensure that the structures are cleaned up in this
    // case, even though we may get here as a jump from random parts of the
    // PNG library called below.
    return false;
  }

  // Set our callback for libpng to give us the data.
  PngEncoderState state(output);
  png_set_write_fn(png_ptr, &state, EncoderWriteCallback, NULL);

  png_set_IHDR(png_ptr, info_ptr, w, h, 8, png_output_color_type,
               PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
               PNG_FILTER_TYPE_DEFAULT);
  png_write_info(png_ptr, info_ptr);

  if (!converter) {
    // No conversion needed, give the data directly to libpng.
    for (int y = 0; y < h; y ++)
      png_write_row(png_ptr,
                    const_cast<unsigned char*>(&input[y * row_byte_width]));
  } else {
    // Needs conversion using a separate buffer.
    unsigned char* row = new unsigned char[w * output_color_components];
    for (int y = 0; y < h; y ++) {
      converter(&input[y * row_byte_width], w, row);
      png_write_row(png_ptr, row);
    }
    delete[] row;
  }

  png_write_end(png_ptr, info_ptr);
  return true;
}

// static
bool PNGEncoder::EncodeBGRASkBitmap(const SkBitmap& input,
                                    bool discard_transparency,
                                    std::vector<unsigned char>* output) {
  // This only works with images with four bytes per pixel.
  static const int bbp = 4;

  DCHECK(input.empty() || input.bytesPerPixel() == bbp);

  // SkBitmaps are premultiplied, we need to unpremultiply them.
  scoped_array<unsigned char> divided(
      new unsigned char[input.width() * input.height() * bbp]);

  SkAutoLockPixels lock_input(input);
  int i = 0;
  for (int y = 0; y < input.height(); y++) {
    uint32* src_row = input.getAddr32(0, y);
    for (int x = 0; x < input.width(); x++) {
      int alpha = SkColorGetA(src_row[x]);
      if (alpha != 0 && alpha != 255) {
        divided[i + 0] = (SkColorGetR(src_row[x]) << 8) / alpha;
        divided[i + 1] = (SkColorGetG(src_row[x]) << 8) / alpha;
        divided[i + 2] = (SkColorGetB(src_row[x]) << 8) / alpha;
        divided[i + 3] = alpha;
      } else {
        divided[i + 0] = SkColorGetR(src_row[x]);
        divided[i + 1] = SkColorGetG(src_row[x]);
        divided[i + 2] = SkColorGetB(src_row[x]);
        divided[i + 3] = SkColorGetA(src_row[x]);
      }
      i += bbp;
    }
  }

  return Encode(divided.release(),
                PNGEncoder::FORMAT_RGBA, input.width(), input.height(),
                input.rowBytes(), discard_transparency, output);
}
<commit_msg>Attempt to fix memory leak again.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/basictypes.h"
#include "base/gfx/png_encoder.h"
#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "third_party/skia/include/core/SkBitmap.h"

extern "C" {
#include "third_party/libpng/png.h"
}

namespace {

// Converts BGRA->RGBA and RGBA->BGRA.
void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width,
                               unsigned char* output) {
  for (int x = 0; x < pixel_width; x++) {
    const unsigned char* pixel_in = &input[x * 4];
    unsigned char* pixel_out = &output[x * 4];
    pixel_out[0] = pixel_in[2];
    pixel_out[1] = pixel_in[1];
    pixel_out[2] = pixel_in[0];
    pixel_out[3] = pixel_in[3];
  }
}

void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width,
                      unsigned char* rgb) {
  for (int x = 0; x < pixel_width; x++) {
    const unsigned char* pixel_in = &rgba[x * 4];
    unsigned char* pixel_out = &rgb[x * 3];
    pixel_out[0] = pixel_in[0];
    pixel_out[1] = pixel_in[1];
    pixel_out[2] = pixel_in[2];
  }
}

}  // namespace

// Encoder --------------------------------------------------------------------
//
// This section of the code is based on nsPNGEncoder.cpp in Mozilla
// (Copyright 2005 Google Inc.)

namespace {

// Passed around as the io_ptr in the png structs so our callbacks know where
// to write data.
struct PngEncoderState {
  PngEncoderState(std::vector<unsigned char>* o) : out(o) {}
  std::vector<unsigned char>* out;
};

// Called by libpng to flush its internal buffer to ours.
void EncoderWriteCallback(png_structp png, png_bytep data, png_size_t size) {
  PngEncoderState* state = static_cast<PngEncoderState*>(png_get_io_ptr(png));
  DCHECK(state->out);

  size_t old_size = state->out->size();
  state->out->resize(old_size + size);
  memcpy(&(*state->out)[old_size], data, size);
}

void ConvertBGRAtoRGB(const unsigned char* bgra, int pixel_width,
                      unsigned char* rgb) {
  for (int x = 0; x < pixel_width; x++) {
    const unsigned char* pixel_in = &bgra[x * 4];
    unsigned char* pixel_out = &rgb[x * 3];
    pixel_out[0] = pixel_in[2];
    pixel_out[1] = pixel_in[1];
    pixel_out[2] = pixel_in[0];
  }
}

// Automatically destroys the given write structs on destruction to make
// cleanup and error handling code cleaner.
class PngWriteStructDestroyer {
 public:
  PngWriteStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {
  }
  ~PngWriteStructDestroyer() {
    png_destroy_write_struct(ps_, pi_);
  }
 private:
  png_struct** ps_;
  png_info** pi_;

  DISALLOW_EVIL_CONSTRUCTORS(PngWriteStructDestroyer);
};

}  // namespace

// static
bool PNGEncoder::Encode(const unsigned char* input, ColorFormat format,
                      int w, int h, int row_byte_width,
                      bool discard_transparency,
                      std::vector<unsigned char>* output) {
  // Run to convert an input row into the output row format, NULL means no
  // conversion is necessary.
  void (*converter)(const unsigned char* in, int w, unsigned char* out) = NULL;

  int input_color_components, output_color_components;
  int png_output_color_type;
  switch (format) {
    case FORMAT_RGB:
      input_color_components = 3;
      output_color_components = 3;
      png_output_color_type = PNG_COLOR_TYPE_RGB;
      discard_transparency = false;
      break;

    case FORMAT_RGBA:
      input_color_components = 4;
      if (discard_transparency) {
        output_color_components = 3;
        png_output_color_type = PNG_COLOR_TYPE_RGB;
        converter = ConvertRGBAtoRGB;
      } else {
        output_color_components = 4;
        png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
        converter = NULL;
      }
      break;

    case FORMAT_BGRA:
      input_color_components = 4;
      if (discard_transparency) {
        output_color_components = 3;
        png_output_color_type = PNG_COLOR_TYPE_RGB;
        converter = ConvertBGRAtoRGB;
      } else {
        output_color_components = 4;
        png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
        converter = ConvertBetweenBGRAandRGBA;
      }
      break;

    default:
      NOTREACHED() << "Unknown pixel format";
      return false;
  }

  // Row stride should be at least as long as the length of the data.
  DCHECK(input_color_components * w <= row_byte_width);

  png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
                                                png_voidp_NULL,
                                                png_error_ptr_NULL,
                                                png_error_ptr_NULL);
  if (!png_ptr)
    return false;
  png_info* info_ptr = png_create_info_struct(png_ptr);
  if (!info_ptr) {
    png_destroy_write_struct(&png_ptr, NULL);
    return false;
  }
  PngWriteStructDestroyer destroyer(&png_ptr, &info_ptr);

  if (setjmp(png_jmpbuf(png_ptr))) {
    // The destroyer will ensure that the structures are cleaned up in this
    // case, even though we may get here as a jump from random parts of the
    // PNG library called below.
    return false;
  }

  // Set our callback for libpng to give us the data.
  PngEncoderState state(output);
  png_set_write_fn(png_ptr, &state, EncoderWriteCallback, NULL);

  png_set_IHDR(png_ptr, info_ptr, w, h, 8, png_output_color_type,
               PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
               PNG_FILTER_TYPE_DEFAULT);
  png_write_info(png_ptr, info_ptr);

  if (!converter) {
    // No conversion needed, give the data directly to libpng.
    for (int y = 0; y < h; y ++)
      png_write_row(png_ptr,
                    const_cast<unsigned char*>(&input[y * row_byte_width]));
  } else {
    // Needs conversion using a separate buffer.
    unsigned char* row = new unsigned char[w * output_color_components];
    for (int y = 0; y < h; y ++) {
      converter(&input[y * row_byte_width], w, row);
      png_write_row(png_ptr, row);
    }
    delete[] row;
  }

  png_write_end(png_ptr, info_ptr);
  return true;
}

// static
bool PNGEncoder::EncodeBGRASkBitmap(const SkBitmap& input,
                                    bool discard_transparency,
                                    std::vector<unsigned char>* output) {
  // This only works with images with four bytes per pixel.
  static const int bbp = 4;

  DCHECK(input.empty() || input.bytesPerPixel() == bbp);

  // SkBitmaps are premultiplied, we need to unpremultiply them.
  scoped_array<unsigned char> divided(
      new unsigned char[input.width() * input.height() * bbp]);

  SkAutoLockPixels lock_input(input);
  int i = 0;
  for (int y = 0; y < input.height(); y++) {
    uint32* src_row = input.getAddr32(0, y);
    for (int x = 0; x < input.width(); x++) {
      int alpha = SkColorGetA(src_row[x]);
      if (alpha != 0 && alpha != 255) {
        divided[i + 0] = (SkColorGetR(src_row[x]) << 8) / alpha;
        divided[i + 1] = (SkColorGetG(src_row[x]) << 8) / alpha;
        divided[i + 2] = (SkColorGetB(src_row[x]) << 8) / alpha;
        divided[i + 3] = alpha;
      } else {
        divided[i + 0] = SkColorGetR(src_row[x]);
        divided[i + 1] = SkColorGetG(src_row[x]);
        divided[i + 2] = SkColorGetB(src_row[x]);
        divided[i + 3] = SkColorGetA(src_row[x]);
      }
      i += bbp;
    }
  }

  return Encode(divided.get(),
                PNGEncoder::FORMAT_RGBA, input.width(), input.height(),
                input.rowBytes(), discard_transparency, output);
}
<|endoftext|>


<commit_before>#include "gm.h"
#include "SkPicture.h"
#include "SkRectShape.h"
#include "SkBlurDrawLooper.h"

namespace skiagm {

///////////////////////////////////////////////////////////////////////////////

class ShadowsGM : public GM {

public:
    SkPath fCirclePath;
    SkPaint fPaint;
    SkRectShape fRectShape;
    ShadowsGM() {
        fCirclePath.addCircle(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(10) );
    fPaint.setStrokeWidth(SkIntToScalar(4));
    fPaint.setAntiAlias(true);
    fPaint.setColor(0xFF00FF00);
    fPaint.setStyle(SkPaint::kStroke_Style);
    SkRect rect;
    rect.set(SkIntToScalar(10), SkIntToScalar(10),
             SkIntToScalar(30), SkIntToScalar(30));
    fRectShape.setRect(rect);
    fRectShape.paint().setColor(SK_ColorRED);
    }

    virtual ~ShadowsGM() {
    }

protected:
    virtual SkString onShortName() {
        return SkString("shadows");
    }

    virtual SkISize onISize() {
        return make_isize(200, 80);
    }

    void drawBG(SkCanvas* canvas) {
        canvas->drawColor(0xFFDDDDDD);
    }

    virtual void onDraw(SkCanvas* canvas) {
        this->drawBG(canvas);

    SkBlurDrawLooper* shadowLoopers[5];
    shadowLoopers[0] =
        new SkBlurDrawLooper (SkIntToScalar(10), SkIntToScalar(5),
                              SkIntToScalar(10), 0xFF0000FF,
                              SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
                              SkBlurDrawLooper::kOverrideColor_BlurFlag |
                              SkBlurDrawLooper::kHighQuality_BlurFlag );
    SkAutoUnref aurL0(shadowLoopers[0]);
    shadowLoopers[1] =
        new SkBlurDrawLooper (SkIntToScalar(10), SkIntToScalar(5),
                              SkIntToScalar(10), 0xFF0000FF,
                              SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
                              SkBlurDrawLooper::kOverrideColor_BlurFlag );
    SkAutoUnref aurL1(shadowLoopers[1]);
    shadowLoopers[2] =
        new SkBlurDrawLooper (SkIntToScalar(5), SkIntToScalar(5),
                              SkIntToScalar(10), 0xFF000000,
                              SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
                              SkBlurDrawLooper::kHighQuality_BlurFlag  );
    SkAutoUnref aurL2(shadowLoopers[2]);
    shadowLoopers[3] =
        new SkBlurDrawLooper (SkIntToScalar(5), SkIntToScalar(-5),
                              SkIntToScalar(-10), 0x7FFF0000,
                              SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
                              SkBlurDrawLooper::kOverrideColor_BlurFlag |
                              SkBlurDrawLooper::kHighQuality_BlurFlag  );
    SkAutoUnref aurL3(shadowLoopers[3]);
    shadowLoopers[4] =
        new SkBlurDrawLooper (SkIntToScalar(0), SkIntToScalar(5),
                              SkIntToScalar(5), 0xFF000000,
                              SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
                              SkBlurDrawLooper::kOverrideColor_BlurFlag |
                              SkBlurDrawLooper::kHighQuality_BlurFlag  );
    SkAutoUnref aurL4(shadowLoopers[4]);

    for (int looper = 0; looper < 5; looper ++)
    {
        fRectShape.paint().setLooper(shadowLoopers[looper]);
        canvas->resetMatrix();
        canvas->translate(SkIntToScalar(looper*40), SkIntToScalar(0));
        canvas->drawShape(&fRectShape);
        fPaint.setLooper(shadowLoopers[looper]);
        canvas->translate(SkIntToScalar(0), SkIntToScalar(40));
        canvas->drawPath(fCirclePath, fPaint);
    }
}

private:
    typedef GM INHERITED;
};

///////////////////////////////////////////////////////////////////////////////

static GM* MyFactory(void*) { return new ShadowsGM; }
static GMRegistry reg(MyFactory);

}
<commit_msg>retool without shapes, which are broken/experimental<commit_after>#include "gm.h"
#include "SkBlurDrawLooper.h"

namespace skiagm {

///////////////////////////////////////////////////////////////////////////////

static void setup(SkPaint* paint, SkColor c, SkScalar strokeWidth) {
    paint->setColor(c);
    if (strokeWidth < 0) {
        paint->setStyle(SkPaint::kFill_Style);
    } else {
        paint->setStyle(SkPaint::kStroke_Style);
        paint->setStrokeWidth(strokeWidth);
    }
}

class ShadowsGM : public GM {
public:
    SkPath fCirclePath;
    SkRect fRect;

    ShadowsGM() {
        fCirclePath.addCircle(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(10) );
        fRect.set(SkIntToScalar(10), SkIntToScalar(10),
                  SkIntToScalar(30), SkIntToScalar(30));
    }

protected:
    virtual SkString onShortName() {
        return SkString("shadows");
    }

    virtual SkISize onISize() {
        return make_isize(200, 80);
    }

    void drawBG(SkCanvas* canvas) {
        canvas->drawColor(0xFFDDDDDD);
    }

    virtual void onDraw(SkCanvas* canvas) {
        this->drawBG(canvas);

    SkBlurDrawLooper* shadowLoopers[5];
    shadowLoopers[0] =
        new SkBlurDrawLooper (SkIntToScalar(10), SkIntToScalar(5),
                              SkIntToScalar(10), 0xFF0000FF,
                              SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
                              SkBlurDrawLooper::kOverrideColor_BlurFlag |
                              SkBlurDrawLooper::kHighQuality_BlurFlag );
    SkAutoUnref aurL0(shadowLoopers[0]);
    shadowLoopers[1] =
        new SkBlurDrawLooper (SkIntToScalar(10), SkIntToScalar(5),
                              SkIntToScalar(10), 0xFF0000FF,
                              SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
                              SkBlurDrawLooper::kOverrideColor_BlurFlag );
    SkAutoUnref aurL1(shadowLoopers[1]);
    shadowLoopers[2] =
        new SkBlurDrawLooper (SkIntToScalar(5), SkIntToScalar(5),
                              SkIntToScalar(10), 0xFF000000,
                              SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
                              SkBlurDrawLooper::kHighQuality_BlurFlag  );
    SkAutoUnref aurL2(shadowLoopers[2]);
    shadowLoopers[3] =
        new SkBlurDrawLooper (SkIntToScalar(5), SkIntToScalar(-5),
                              SkIntToScalar(-10), 0x7FFF0000,
                              SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
                              SkBlurDrawLooper::kOverrideColor_BlurFlag |
                              SkBlurDrawLooper::kHighQuality_BlurFlag  );
    SkAutoUnref aurL3(shadowLoopers[3]);
    shadowLoopers[4] =
        new SkBlurDrawLooper (SkIntToScalar(0), SkIntToScalar(5),
                              SkIntToScalar(5), 0xFF000000,
                              SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
                              SkBlurDrawLooper::kOverrideColor_BlurFlag |
                              SkBlurDrawLooper::kHighQuality_BlurFlag  );
    SkAutoUnref aurL4(shadowLoopers[4]);

    static const struct {
        SkColor fColor;
        SkScalar fStrokeWidth;
    } gRec[] = {
        { SK_ColorRED,      -SK_Scalar1 },
        { SK_ColorGREEN,    SkIntToScalar(4) },
    };

    SkPaint paint;
    paint.setAntiAlias(true);
    for (size_t i = 0; i < SK_ARRAY_COUNT(shadowLoopers); ++i) {
        SkAutoCanvasRestore acr(canvas, true);

        paint.setLooper(shadowLoopers[i]);

        canvas->translate(SkIntToScalar(i*40), SkIntToScalar(0));
        setup(&paint, gRec[0].fColor, gRec[0].fStrokeWidth);
        canvas->drawRect(fRect, paint);

        canvas->translate(SkIntToScalar(0), SkIntToScalar(40));
        setup(&paint, gRec[1].fColor, gRec[1].fStrokeWidth);
        canvas->drawPath(fCirclePath, paint);
    }
}

private:
    typedef GM INHERITED;
};

///////////////////////////////////////////////////////////////////////////////

static GM* MyFactory(void*) { return new ShadowsGM; }
static GMRegistry reg(MyFactory);

}
<|endoftext|>


<commit_before>// @(#)root/cont:$Name:  $:$Id: TObjectRef.cxx,v 1.1 2001/10/01 10:29:08 brun Exp $
// Author: Rene Brun   28/09/2001

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TObjectRef                                                           //
//                                                                      //
// Persistent Reference link to a TObject                               //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TObjectRef.h"
#include "TROOT.h"
#include "TProcessID.h"
#include "TFile.h"
#include "TObjArray.h"
#include "TSystem.h"

ClassImp(TObjectRef)

// A TObjectRef is a lightweight object pointing to any TObject.
// This object can be used instead of normal C++ pointers in case
//  - the referenced object R and the pointer P are not written to the same file
//  - P is read before R
//  - R and P are written to different Tree branches
//
// When a top level object (eg Event *event) is a tree/graph of many objects,
// the normal ROOT Streaming mechanism ensures that only one copy of each object
// in the tree/graph is written to the output buffer to avoid circular
// dependencies.
// However if the object event is split into several files or into several
// branches of one or more Trees, normal C++ pointers cannot be used because
// each I/O operation will write the referenced objects.
// When a TObjectRef is used to point to a TObject *robj.
//For example in a class with
//     TObjectRef  fRef;
// one can do:
//   fRef = robj;  //to set the pointer
// this TObjectRef and robj can be written with two different I/O calls
// in the same or different files, in the same or different branches of a Tree.
// If the TObjectRef is read and the referenced object has not yet been read,
// the TObjectRef will return a null pointer. As soon as the referenced object
// will be read, the TObjectRef will point to it.
//
// TObjectRef also supports the complex situation where a TFile is updated
// multiple times on the same machine or a different machine.
//
// How does it work
// ----------------
// A TObjectRef is itself a TObject with an additional transient pointer fPID.
// When the statement fRef = robj is executed, the fRef::fUniqueID is set
// to the value "obj-gSystem". This uid is in general a small integer, even
// on a 64 bit system.
// After having set fRef, one can immediatly return the value of robj
// with "gSystem + uid" using fRef.GetObject() or the dereferencing operator ->.
//
// When the TObjectRef is written, the process id number pidf  (see TProcessID)
// is written as well as the uid.
// When the TObjectRef is read, its pointer fPID is set to the value
// stored in the TObjArray of TFile::fProcessIDs (fProcessIDs[pidf]).
//
// When a referenced object robj is written, TObject::Streamer writes
// in addition to the standard (fBits,fUniqueID) the pair uid,pidf.
// When this robj is read by TObject::Streamer, the pair uid,pidf is read.
// At this point, robj is entered into the TExmap of the TProcessID
// corresponding to pidf.
//
// Once the referenced object robj is in memory, TObjectRef::GetObject will
// store the object pointer robj-gSystem  into the fUniqueID such that
// the next access to the pointer will be fast (no need to search in
// the TExMap of the TProcessID anymore).
//
// Implicit use of TObjectRef in ROOT collections
// ----------------------------------------------
// The TSeqCollection (TList, TObjArray, etc) have been modified with
// additional member functions AddRef, AddRefAt, etc to create automatically
// a TObjectRef when doing, eg:
//    myArray->AddRef(robj);
//
// Example:
// Suppose a TObjArray *mytracks containing a list of Track objects
// Suppose a TObjArray *pions containing pointers to the pion tracks in mytracks.
//   This list is created with statements like: pions->AddRef(track);
// Suppose a TObjArray *muons containing pointers to the muon tracks in mytracks.
// The 3 arrays mytracks,pions and muons may be written separately.
//

//______________________________________________________________________________
TObjectRef::TObjectRef(TObject *obj)
{
   // TObjectRef copy ctor.

   *this = obj;
   fPID = 0;
}

//______________________________________________________________________________
TObjectRef::TObjectRef(const TObjectRef &ref)
{
}

//______________________________________________________________________________
void TObjectRef::operator=(TObject *obj)
{
   // TObjectRef assignment operator.

   UInt_t uid;
   if (obj) {
      obj->SetBit(kIsReferenced);
      uid = (char*)obj - (char*)gSystem;
   } else {
      uid = 0;
   }
   SetUniqueID(uid);
}

//______________________________________________________________________________
TObject *TObjectRef::GetObject()
{
   // Return a pointer to the referenced object.

   TObject *obj = 0;
   Long_t uid = (Long_t)GetUniqueID();
   if (uid == 0) return obj;
   if (!TestBit(1)) return (TObject*)(uid + (char*)gSystem);
   if (!fPID) return 0;
   obj = fPID->GetObjectWithID(uid);
   if (obj) {
      ResetBit(1);
      uid = (char*)obj - (char*)gSystem;
      SetUniqueID((UInt_t)uid);
   }
   return obj;
}

//______________________________________________________________________________
void TObjectRef::ReadRef(TObject *obj, TBuffer &R__b, TFile *file)
{
// static function

   Long_t uid;
   Int_t pidf = 0;
   R__b >> uid;
   R__b >> pidf;

   TProcessID *pid = TProcessID::ReadProcessID(pidf,file);

   if (pid) pid->PutObjectWithID(uid,obj);
}

//______________________________________________________________________________
void TObjectRef::SaveRef(TObject *obj, TBuffer &R__b, TFile *file)
{
// static function

   Long_t uid = (char*)obj - (char*)gSystem;
   Int_t pidf = 0;
   if (file) {
      pidf = file->GetProcessCount();
   }
   R__b << uid;
   R__b << pidf;
}

//______________________________________________________________________________
void TObjectRef::Streamer(TBuffer &R__b)
{
   // Stream an object of class TObjectRef.

   Long_t uid;
   Int_t pidf;
   if (R__b.IsReading()) {
      R__b >> pidf;
      R__b >> uid;
      SetBit(1,1);
      SetUniqueID((UInt_t)uid);
      fPID = TProcessID::ReadProcessID(pidf,gFile);
   } else {
      uid = (Long_t)GetUniqueID();
      if (gFile) pidf = gFile->GetProcessCount();
      else       pidf = 0;
      R__b << pidf;
      R__b << uid;
   }
}
<commit_msg>When a referenced object is written, the TFile bit kHasReferences is set.<commit_after>// @(#)root/cont:$Name:  $:$Id: TObjectRef.cxx,v 1.2 2001/10/02 08:16:23 brun Exp $
// Author: Rene Brun   28/09/2001

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TObjectRef                                                           //
//                                                                      //
// Persistent Reference link to a TObject                               //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TObjectRef.h"
#include "TROOT.h"
#include "TProcessID.h"
#include "TFile.h"
#include "TObjArray.h"
#include "TSystem.h"

ClassImp(TObjectRef)

// A TObjectRef is a lightweight object pointing to any TObject.
// This object can be used instead of normal C++ pointers in case
//  - the referenced object R and the pointer P are not written to the same file
//  - P is read before R
//  - R and P are written to different Tree branches
//
// When a top level object (eg Event *event) is a tree/graph of many objects,
// the normal ROOT Streaming mechanism ensures that only one copy of each object
// in the tree/graph is written to the output buffer to avoid circular
// dependencies.
// However if the object event is split into several files or into several
// branches of one or more Trees, normal C++ pointers cannot be used because
// each I/O operation will write the referenced objects.
// When a TObjectRef is used to point to a TObject *robj.
//For example in a class with
//     TObjectRef  fRef;
// one can do:
//   fRef = robj;  //to set the pointer
// this TObjectRef and robj can be written with two different I/O calls
// in the same or different files, in the same or different branches of a Tree.
// If the TObjectRef is read and the referenced object has not yet been read,
// the TObjectRef will return a null pointer. As soon as the referenced object
// will be read, the TObjectRef will point to it.
//
// TObjectRef also supports the complex situation where a TFile is updated
// multiple times on the same machine or a different machine.
//
// How does it work
// ----------------
// A TObjectRef is itself a TObject with an additional transient pointer fPID.
// When the statement fRef = robj is executed, the fRef::fUniqueID is set
// to the value "obj-gSystem". This uid is in general a small integer, even
// on a 64 bit system.
// After having set fRef, one can immediatly return the value of robj
// with "gSystem + uid" using fRef.GetObject() or the dereferencing operator ->.
//
// When the TObjectRef is written, the process id number pidf  (see TProcessID)
// is written as well as the uid.
// When the TObjectRef is read, its pointer fPID is set to the value
// stored in the TObjArray of TFile::fProcessIDs (fProcessIDs[pidf]).
//
// When a referenced object robj is written, TObject::Streamer writes
// in addition to the standard (fBits,fUniqueID) the pair uid,pidf.
// When this robj is read by TObject::Streamer, the pair uid,pidf is read.
// At this point, robj is entered into the TExmap of the TProcessID
// corresponding to pidf.
//
// Once the referenced object robj is in memory, TObjectRef::GetObject will
// store the object pointer robj-gSystem  into the fUniqueID such that
// the next access to the pointer will be fast (no need to search in
// the TExMap of the TProcessID anymore).
//
// WARNING: If MyClass is the class of the referenced object, The TObject
//          part of MyClass must be Streamed. One should not
//          call MyClass::Class()->IgnoreTObjectStreamer()
//
// Implicit use of TObjectRef in ROOT collections
// ----------------------------------------------
// The TSeqCollection (TList, TObjArray, etc) have been modified with
// additional member functions AddRef, AddRefAt, etc to create automatically
// a TObjectRef when doing, eg:
//    myArray->AddRef(robj);
//
// Example:
// Suppose a TObjArray *mytracks containing a list of Track objects
// Suppose a TObjArray *pions containing pointers to the pion tracks in mytracks.
//   This list is created with statements like: pions->AddRef(track);
// Suppose a TObjArray *muons containing pointers to the muon tracks in mytracks.
// The 3 arrays mytracks,pions and muons may be written separately.
//

//______________________________________________________________________________
TObjectRef::TObjectRef(TObject *obj)
{
   // TObjectRef copy ctor.

   *this = obj;
   fPID = 0;
}

//______________________________________________________________________________
TObjectRef::TObjectRef(const TObjectRef &ref)
{
}

//______________________________________________________________________________
void TObjectRef::operator=(TObject *obj)
{
   // TObjectRef assignment operator.

   UInt_t uid;
   if (obj) {
      if (obj->IsA()->CanIgnoreTObjectStreamer()) {
         Error("operator= ","Class: %s IgnoreTObjectStreamer. Cannot reference object",obj->ClassName());
         return;
      }
      obj->SetBit(kIsReferenced);
      uid = (char*)obj - (char*)gSystem;
   } else {
      uid = 0;
   }
   SetUniqueID(uid);
}

//______________________________________________________________________________
TObject *TObjectRef::GetObject()
{
   // Return a pointer to the referenced object.

   TObject *obj = 0;
   Long_t uid = (Long_t)GetUniqueID();
   if (uid == 0) return obj;
   if (!TestBit(1)) return (TObject*)(uid + (char*)gSystem);
   if (!fPID) return 0;
   obj = fPID->GetObjectWithID(uid);
   if (obj) {
      ResetBit(1);
      uid = (char*)obj - (char*)gSystem;
      SetUniqueID((UInt_t)uid);
   }
   return obj;
}

//______________________________________________________________________________
void TObjectRef::ReadRef(TObject *obj, TBuffer &R__b, TFile *file)
{
// static function

   Long_t uid;
   Int_t pidf = 0;
   R__b >> uid;
   R__b >> pidf;

   TProcessID *pid = TProcessID::ReadProcessID(pidf,file);

   if (pid) pid->PutObjectWithID(uid,obj);
}

//______________________________________________________________________________
void TObjectRef::SaveRef(TObject *obj, TBuffer &R__b, TFile *file)
{
// static function

   Long_t uid = (char*)obj - (char*)gSystem;
   Int_t pidf = 0;
   if (file) {
      pidf = file->GetProcessCount();
      file->SetBit(TFile::kHasReferences);
   }
   R__b << uid;
   R__b << pidf;
}

//______________________________________________________________________________
void TObjectRef::Streamer(TBuffer &R__b)
{
   // Stream an object of class TObjectRef.

   Long_t uid;
   Int_t pidf;
   if (R__b.IsReading()) {
      R__b >> pidf;
      R__b >> uid;
      SetBit(1,1);
      SetUniqueID((UInt_t)uid);
      fPID = TProcessID::ReadProcessID(pidf,gFile);
   } else {
      pidf = 0;
      uid = (Long_t)GetUniqueID();
      if (gFile) {
         pidf = gFile->GetProcessCount();
         gFile->SetBit(TFile::kHasReferences);
      }
      R__b << pidf;
      R__b << uid;
   }
}
<|endoftext|>


<commit_before>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
(Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2009 Torus Knot Software Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/

#include "OgreCgPlugin.h"
#include "OgreRoot.h"
#include "OgreHighLevelGpuProgramManager.h"
#include "OgreCgFxScriptLoader.h"

namespace Ogre
{
	const String sPluginName = "Cg Program Manager";
	//---------------------------------------------------------------------
	CgPlugin::CgPlugin()
		:mCgProgramFactory(0)
	{

	}
	//---------------------------------------------------------------------
	const String& CgPlugin::getName() const
	{
		return sPluginName;
	}
	//---------------------------------------------------------------------
	void CgPlugin::install()
	{
		// Create new factory
		mCgProgramFactory = OGRE_NEW CgProgramFactory();
		// Register
		HighLevelGpuProgramManager::getSingleton().addFactory(mCgProgramFactory);

		OGRE_NEW CgFxScriptLoader();
	}
	//---------------------------------------------------------------------
	void CgPlugin::initialise()
	{
		// nothing to do
	}
	//---------------------------------------------------------------------
	void CgPlugin::shutdown()
	{
		// nothing to do
	}
	//---------------------------------------------------------------------
	void CgPlugin::uninstall()
	{
        if (mCgProgramFactory)
        {
			OGRE_DELETE CgFxScriptLoader::getSingletonPtr();

            // Remove from manager safely
            if (HighLevelGpuProgramManager::getSingletonPtr())
                HighLevelGpuProgramManager::getSingleton().removeFactory(mCgProgramFactory);
		    OGRE_DELETE mCgProgramFactory;
		    mCgProgramFactory = 0;
        }
	}

}
<commit_msg>Fixed so the CG plugin will not register a factory when the GLES2 render system is the active render system.<commit_after>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
(Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2009 Torus Knot Software Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/

#include "OgreCgPlugin.h"
#include "OgreRoot.h"
#include "OgreHighLevelGpuProgramManager.h"
#include "OgreCgFxScriptLoader.h"

namespace Ogre
{
	const String sPluginName = "Cg Program Manager";
	//---------------------------------------------------------------------
	CgPlugin::CgPlugin()
		:mCgProgramFactory(0)
	{

	}
	//---------------------------------------------------------------------
	const String& CgPlugin::getName() const
	{
		return sPluginName;
	}
	//---------------------------------------------------------------------
	void CgPlugin::install()
	{
	}
	//---------------------------------------------------------------------
	void CgPlugin::initialise()
	{
        // check for gles2 by the glsles factory (this plugin is not supported on embedded systems for now)
        if (HighLevelGpuProgramManager::getSingleton().isLanguageSupported("glsles") == false)
        {
            // Create new factory
            mCgProgramFactory = OGRE_NEW CgProgramFactory();

            // Register
            HighLevelGpuProgramManager::getSingleton().addFactory(mCgProgramFactory);

            OGRE_NEW CgFxScriptLoader();
        }
	}
	//---------------------------------------------------------------------
	void CgPlugin::shutdown()
	{
		// nothing to do
	}
	//---------------------------------------------------------------------
	void CgPlugin::uninstall()
	{
        if (mCgProgramFactory)
        {
			OGRE_DELETE CgFxScriptLoader::getSingletonPtr();

            // Remove from manager safely
            if (HighLevelGpuProgramManager::getSingletonPtr())
                HighLevelGpuProgramManager::getSingleton().removeFactory(mCgProgramFactory);
		    OGRE_DELETE mCgProgramFactory;
		    mCgProgramFactory = 0;
        }
	}
}
<|endoftext|>


<commit_before>#ifndef GAME_CONFIG_HPP
#define GAME_CONFIG_HPP

/*
 *  Global config values for easy tweaking and changing
 */

// Maximum orbs spawned on screen
static const int MAX_ORBS = 2;

// variables for borders that restricts movement
static const float RING_INNER = 100.f;
static const float RING_OUTER = 300.f;

//Logic variables for world height and width
static const int WORLD_WIDTH = 1600;
static const int WORLD_HEIGHT = 1200;

//screen width and height
static const int WIDTH = 800;
static const int HEIGHT = 600;

static const float MAX_SPEED = 200.f;

static const bool DEBUG = true;
static const int DEBUG_START_LEVEL = 1; // index of first level

#endif
<commit_msg>Start at level 0<commit_after>#ifndef GAME_CONFIG_HPP
#define GAME_CONFIG_HPP

/*
 *  Global config values for easy tweaking and changing
 */

// Maximum orbs spawned on screen
static const int MAX_ORBS = 2;

// variables for borders that restricts movement
static const float RING_INNER = 100.f;
static const float RING_OUTER = 300.f;

//Logic variables for world height and width
static const int WORLD_WIDTH = 1600;
static const int WORLD_HEIGHT = 1200;

//screen width and height
static const int WIDTH = 800;
static const int HEIGHT = 600;

static const float MAX_SPEED = 200.f;

static const bool DEBUG = true;
static const int DEBUG_START_LEVEL = 0; // index of first level

#endif
<|endoftext|>


<commit_before>#pragma once

struct ScreenMessage {
	enum Action {Push, Pop, Clear}

	static ScreenMessage Push(int id) {
		action = Push;
		screen = id;
	}

	static ScreenMessage Pop() {
		action = Pop;
		screen = -1;
	}

	static ScreenMessage Clear() {
		action = Clear;
		screen = -1;
	}

	int screen;
	Action action;
};
<commit_msg>Fix syntax.<commit_after>#pragma once

struct ScreenMessage {
	enum Action {Push, Pop, Clear};

	static ScreenMessage Push(int id) {
		action = Push;
		screen = id;
	}

	static ScreenMessage Pop() {
		action = Pop;
		screen = -1;
	}

	static ScreenMessage Clear() {
		action = Clear;
		screen = -1;
	}

	int screen;
	Action action;
};
<|endoftext|>


<commit_before>#include "coroutines/io_events.h"
#include "coroutines/io_channel.h"

extern void dbg(const char *fmt, ...);
//#define dbg(...)

#ifdef _WIN32

#pragma comment(lib, "Ws2_32.lib")
#define sys_socket(x,y,z,port)	::socket( x, y, z )
#define sys_connect(id,addr,sz) ::connect( id, (const sockaddr*) addr, sz )
#define sys_send                ::send
#define sys_recv                ::recv
#define sys_errno               ::WSAGetLastError()
#define sys_close               ::closesocket
#define sys_bind(id,addr,sz)    ::bind(id, (const sockaddr *) addr, sz )
#define sys_accept(id,addr,sz)  ::accept( id, (sockaddr*) addr, sz )
#define sys_listen              ::listen

#define SYS_ERR_WOULD_BLOCK      WSAEWOULDBLOCK
#define SYS_ERR_CONN_IN_PROGRESS WSAEWOULDBLOCK

#else

#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <arpa/inet.h>

#define sys_socket(x,y,z,port)  ::socket( x, y, z )
#define sys_connect(id,addr,sz) ::connect( id, (const sockaddr*) addr, sz )
#define sys_send                ::send
#define sys_recv                ::recv
#define sys_errno               errno
#define sys_close               ::close
#define sys_bind(id,addr,sz)    ::bind(id, (const sockaddr *) addr, sz )
#define sys_accept(id,addr,sz)  ::accept( id, (sockaddr*) addr, (socklen_t*)sz )
#define sys_listen              ::listen

#define SYS_ERR_WOULD_BLOCK      EWOULDBLOCK
#define SYS_ERR_CONN_IN_PROGRESS EINPROGRESS

#endif

namespace Coroutines {

  bool CIOChannel::setNonBlocking() {
    // set non-blocking
#if defined(O_NONBLOCK)
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1)
      flags = 0;
    auto rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
#else
    u_long iMode = 1;
    auto rc = ioctlsocket(fd, FIONBIO, &iMode);
#endif
    if (rc != 0)
      dbg("Failed to set socket %d as non-blocking\n", fd);
    return rc == 0;
  }

  // ---------------------------------------------------------------------------
  int CIOChannel::getSocketError() {
    // Confirm we are really connected by checking the socket error
    int sock_err = 0;
    socklen_t sock_err_sz = sizeof(sock_err);
    int rc = getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*)&sock_err, &sock_err_sz);
    return sock_err;
  }

  // ---------------------------------------------------------------------------
  bool CIOChannel::listen(const TNetAddress& serving_addr) {
    if (isValid())
      return false;

    auto new_fd = sys_socket(AF_INET, SOCK_STREAM, 0, 0);
    if (new_fd < 0)
      return false;
    fd = new_fd;

    if (sys_bind(fd, &serving_addr, sizeof(serving_addr)) < 0)
      return false;

    if (sys_listen(fd, 5) < 0)
      return false;

    setNonBlocking();

    return true;
  }

  // ---------------------------------------------------------------------------
  CIOChannel CIOChannel::accept() {
    dbg("FD %d is accepting connections\n", fd);

    while (isValid()) {
      TNetAddress remote_client_addr;
      int remote_addr_sz = sizeof(remote_client_addr);
      int rc = sys_accept(fd, &remote_client_addr.addr, &remote_addr_sz);
      if (rc < 0) {
        int sys_err = sys_errno;
        if (sys_err == SYS_ERR_WOULD_BLOCK) {
          dbg("FD %d goes to sleep waiting for a connection\n", fd);
          TWatchedEvent we(fd, EVT_SOCKET_IO_CAN_READ);
          wait(&we, 1);
          continue;
        }
        dbg("FD %d accept failed (%08x vs %08x)\n", fd, sys_err, SYS_ERR_WOULD_BLOCK);
        // Other types of errors
        return CIOChannel();
      }
      dbg("FD %d has accepted new client %d\n", fd, rc);
      CIOChannel new_client;
      new_client.fd = rc;
      new_client.setNonBlocking();
      return new_client;
    }
    return CIOChannel();
  }

  // ---------------------------------------------------------------------------
  bool CIOChannel::connect(const TNetAddress &remote_server, int timeout_sec) {

    auto new_fd = sys_socket(AF_INET, SOCK_STREAM, 0, 0);
    if (new_fd < 0)
      return false;
    fd = new_fd;

    setNonBlocking();

    if (0) {
      dbg("SYS_ERR_WOULD_BLOCK = %08x\n", SYS_ERR_WOULD_BLOCK);
      dbg("SYS_ERR_CONN_IN_PROGRESS = %08x\n", SYS_ERR_CONN_IN_PROGRESS);
      dbg("SYS_ERR_CONN_REFUSED = %08x\n", ECONNREFUSED);
    }

    dbg("FD %d starting to connect\n", fd);

    while (isValid()) {
      int rc = sys_connect(fd, &remote_server.addr, sizeof(remote_server));
      if (rc < 0) {
        int sys_err = sys_errno;
        if (sys_err == SYS_ERR_CONN_IN_PROGRESS) {
          TWatchedEvent we(fd, EVT_SOCKET_IO_CAN_WRITE);
          int n = wait(&we, 1);
          if (n == 0) {

            // Confirm we are really connected by checking the socket error
            int sock_err = getSocketError();

            // All ok, no errors
            if (sock_err == 0)
              break;

            // The expected error in this case is Conn Refused when there is no server
            // in the remote address. Other erros, I prefer to report them
            if (sock_err != ECONNREFUSED)
              dbg("connect.failed getsockopt( %d ) (err=%08x)\n", fd, sock_err);
          }
        }
        dbg("FD %d connect rc = %d (%08x vs %08x)\n", fd, rc, sys_err, SYS_ERR_CONN_IN_PROGRESS);
      }
      else {
        // Connected without waiting
        break;
      }
    }

    // If we are not valid, the socket we created should be destroyed.
    if (!isValid())
      close();
    else
      dbg("FD %d connected\n", fd);
    return isValid();
  }

  // ---------------------------------------------------------------------------
  void CIOChannel::close() {
    if (isValid()) {
      dbg("FD %d closed\n", fd);
      sys_close(fd);
      fd = invalid_socket_id;

      // Remove from entries...
    }
  }

  // ---------------------------------------------------------------------------
  bool CIOChannel::recv(void* dest_buffer, size_t bytes_to_read) const {
    assert(bytes_to_read > 0);
    size_t total_bytes_read = 0;
    while (isValid()) {
      assert(bytes_to_read > total_bytes_read);
      auto new_bytes_read = sys_recv(fd, (char*)dest_buffer, (int)(bytes_to_read - total_bytes_read), 0);
      if (new_bytes_read == -1) {
        int err = sys_errno;
        if (err == SYS_ERR_WOULD_BLOCK) {
          TWatchedEvent we(fd, EVT_SOCKET_IO_CAN_READ);
          wait(&we, 1);
        }
        else
          break;
      }
      else if (new_bytes_read == 0) {
        break;
      }
      else {
        total_bytes_read += new_bytes_read;
        if (total_bytes_read == bytes_to_read)
          return true;
      }
    }
    return false;
  }

  // ---------------------------------------------------------------------------
  int CIOChannel::recvUpTo(void* dest_buffer, size_t bytes_to_read) const {
    while (isValid()) {
      auto new_bytes_read = sys_recv(fd, (char*)dest_buffer, (int)(bytes_to_read), 0);
      if (new_bytes_read == -1) {
        int err = sys_errno;
        if (err == SYS_ERR_WOULD_BLOCK) {
          TWatchedEvent we(fd, EVT_SOCKET_IO_CAN_READ);
          wait(&we, 1);
        }
        else
          break;
      }
      else {
        return new_bytes_read;
      }
    }
    return -1;
  }

  // ---------------------------------------------------------------------------
  bool CIOChannel::send(const void* src_buffer, size_t bytes_to_send) const {
    assert(bytes_to_send > 0);
    size_t total_bytes_sent = 0;
    while (isValid()) {
      assert(bytes_to_send > total_bytes_sent);
      auto bytes_sent = sys_send(fd, ((const char*)src_buffer) + total_bytes_sent, (int)(bytes_to_send - total_bytes_sent), 0);
      if (bytes_sent == -1) {
        if (errno == SYS_ERR_WOULD_BLOCK) {
          TWatchedEvent we(fd, EVT_SOCKET_IO_CAN_WRITE);
          wait(&we, 1);
        }
        else
          break;
      }
      else {
        //dbg("FD %d sent %ld bytes\n", fd, bytes_sent);
        total_bytes_sent += bytes_sent;
        if (total_bytes_sent == bytes_to_send)
          return true;
      }
    }
    return false;
  }
}

// -----------------------------------------------
// port and vport are in host format
void TNetAddress::from(int port, unsigned ip4_in_host_format) {
  assert(port > 0 && port < 65535);
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(ip4_in_host_format);
  addr.sin_port = htons(port);
}

void TNetAddress::setPort(unsigned short new_app_port) {
  addr.sin_port = htons(new_app_port);
}

void TNetAddress::fromAnyAddress(int port) {
  from(port, INADDR_ANY);
}

bool TNetAddress::fromStr(const char *addr_str, int port) {
  fromAnyAddress(port);
  return inet_pton(AF_INET, addr_str, &addr.sin_addr) == 1;
}

<commit_msg>Added strerror<commit_after>#include "coroutines/io_events.h"
#include "coroutines/io_channel.h"

extern void dbg(const char *fmt, ...);
//#define dbg(...)

#ifdef _WIN32

#pragma comment(lib, "Ws2_32.lib")
#define sys_socket(x,y,z,port)	::socket( x, y, z )
#define sys_connect(id,addr,sz) ::connect( id, (const sockaddr*) addr, sz )
#define sys_send                ::send
#define sys_recv                ::recv
#define sys_errno               ::WSAGetLastError()
#define sys_close               ::closesocket
#define sys_bind(id,addr,sz)    ::bind(id, (const sockaddr *) addr, sz )
#define sys_accept(id,addr,sz)  ::accept( id, (sockaddr*) addr, sz )
#define sys_listen              ::listen

#define SYS_ERR_WOULD_BLOCK      WSAEWOULDBLOCK
#define SYS_ERR_CONN_IN_PROGRESS WSAEWOULDBLOCK

#else

#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <arpa/inet.h>

#define sys_socket(x,y,z,port)  ::socket( x, y, z )
#define sys_connect(id,addr,sz) ::connect( id, (const sockaddr*) addr, sz )
#define sys_send                ::send
#define sys_recv                ::recv
#define sys_errno               errno
#define sys_close               ::close
#define sys_bind(id,addr,sz)    ::bind(id, (const sockaddr *) addr, sz )
#define sys_accept(id,addr,sz)  ::accept( id, (sockaddr*) addr, (socklen_t*)sz )
#define sys_listen              ::listen

#define SYS_ERR_WOULD_BLOCK      EWOULDBLOCK
#define SYS_ERR_CONN_IN_PROGRESS EINPROGRESS

#endif

namespace Coroutines {

  bool CIOChannel::setNonBlocking() {
    // set non-blocking
#if defined(O_NONBLOCK)
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1)
      flags = 0;
    auto rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
#else
    u_long iMode = 1;
    auto rc = ioctlsocket(fd, FIONBIO, &iMode);
#endif
    if (rc != 0)
      dbg("Failed to set socket %d as non-blocking\n", fd);
    return rc == 0;
  }

  // ---------------------------------------------------------------------------
  int CIOChannel::getSocketError() {
    // Confirm we are really connected by checking the socket error
    int sock_err = 0;
    socklen_t sock_err_sz = sizeof(sock_err);
    int rc = getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*)&sock_err, &sock_err_sz);
    return sock_err;
  }

  // ---------------------------------------------------------------------------
  bool CIOChannel::listen(const TNetAddress& serving_addr) {
    if (isValid())
      return false;

    auto new_fd = sys_socket(AF_INET, SOCK_STREAM, 0, 0);
    if (new_fd < 0)
      return false;
    fd = new_fd;

    if (sys_bind(fd, &serving_addr, sizeof(serving_addr)) < 0)
      return false;

    if (sys_listen(fd, 5) < 0)
      return false;

    setNonBlocking();

    return true;
  }

  // ---------------------------------------------------------------------------
  CIOChannel CIOChannel::accept() {
    dbg("FD %d is accepting connections\n", fd);

    while (isValid()) {
      TNetAddress remote_client_addr;
      int remote_addr_sz = sizeof(remote_client_addr);
      int rc = sys_accept(fd, &remote_client_addr.addr, &remote_addr_sz);
      if (rc < 0) {
        int sys_err = sys_errno;
        if (sys_err == SYS_ERR_WOULD_BLOCK) {
          dbg("FD %d goes to sleep waiting for a connection\n", fd);
          TWatchedEvent we(fd, EVT_SOCKET_IO_CAN_READ);
          wait(&we, 1);
          continue;
        }
        dbg("FD %d accept failed (%08x %s)\n", fd, sys_err, strerror(sys_err) );
        // Other types of errors
        return CIOChannel();
      }
      dbg("FD %d has accepted new client %d\n", fd, rc);
      CIOChannel new_client;
      new_client.fd = rc;
      new_client.setNonBlocking();
      return new_client;
    }
    return CIOChannel();
  }

  // ---------------------------------------------------------------------------
  bool CIOChannel::connect(const TNetAddress &remote_server, int timeout_sec) {

    auto new_fd = sys_socket(AF_INET, SOCK_STREAM, 0, 0);
    if (new_fd < 0)
      return false;
    fd = new_fd;

    setNonBlocking();

    dbg("FD %d starting to connect\n", fd);

    while (isValid()) {
      int rc = sys_connect(fd, &remote_server.addr, sizeof(remote_server));
      if (rc < 0) {
        int sys_err = sys_errno;
        if (sys_err == SYS_ERR_CONN_IN_PROGRESS) {
          TWatchedEvent we(fd, EVT_SOCKET_IO_CAN_WRITE);
          int n = wait(&we, 1);
          if (n == 0) {

            // Confirm we are really connected by checking the socket error
            int sock_err = getSocketError();

            // All ok, no errors
            if (sock_err == 0)
              break;

            // The expected error in this case is Conn Refused when there is no server
            // in the remote address. Other erros, I prefer to report them
            if (sock_err != ECONNREFUSED)
              dbg("connect.failed getsockopt( %d ) (err=%08x)\n", fd, sock_err);
          }
          continue;
        }
        dbg("FD %d connect rc = %d (%08x %s)\n", fd, rc, sys_err, strerror( sys_err ));
      }
      else {
        // Connected without waiting
        break;
      }
    }

    // If we are not valid, the socket we created should be destroyed.
    if (!isValid())
      close();
    else
      dbg("FD %d connected\n", fd);
    return isValid();
  }

  // ---------------------------------------------------------------------------
  void CIOChannel::close() {
    if (isValid()) {
      dbg("FD %d closed\n", fd);
      sys_close(fd);
      fd = invalid_socket_id;

      // Remove from entries...
    }
  }

  // ---------------------------------------------------------------------------
  bool CIOChannel::recv(void* dest_buffer, size_t bytes_to_read) const {
    assert(bytes_to_read > 0);
    size_t total_bytes_read = 0;
    while (isValid()) {
      assert(bytes_to_read > total_bytes_read);
      auto new_bytes_read = sys_recv(fd, (char*)dest_buffer, (int)(bytes_to_read - total_bytes_read), 0);
      if (new_bytes_read == -1) {
        int err = sys_errno;
        if (err == SYS_ERR_WOULD_BLOCK) {
          TWatchedEvent we(fd, EVT_SOCKET_IO_CAN_READ);
          wait(&we, 1);
        }
        else
          break;
      }
      else if (new_bytes_read == 0) {
        break;
      }
      else {
        total_bytes_read += new_bytes_read;
        if (total_bytes_read == bytes_to_read)
          return true;
      }
    }
    return false;
  }

  // ---------------------------------------------------------------------------
  int CIOChannel::recvUpTo(void* dest_buffer, size_t bytes_to_read) const {
    while (isValid()) {
      auto new_bytes_read = sys_recv(fd, (char*)dest_buffer, (int)(bytes_to_read), 0);
      if (new_bytes_read == -1) {
        int err = sys_errno;
        if (err == SYS_ERR_WOULD_BLOCK) {
          TWatchedEvent we(fd, EVT_SOCKET_IO_CAN_READ);
          wait(&we, 1);
        }
        else
          break;
      }
      else {
        return new_bytes_read;
      }
    }
    return -1;
  }

  // ---------------------------------------------------------------------------
  bool CIOChannel::send(const void* src_buffer, size_t bytes_to_send) const {
    assert(bytes_to_send > 0);
    size_t total_bytes_sent = 0;
    while (isValid()) {
      assert(bytes_to_send > total_bytes_sent);
      auto bytes_sent = sys_send(fd, ((const char*)src_buffer) + total_bytes_sent, (int)(bytes_to_send - total_bytes_sent), 0);
      if (bytes_sent == -1) {
        if (errno == SYS_ERR_WOULD_BLOCK) {
          TWatchedEvent we(fd, EVT_SOCKET_IO_CAN_WRITE);
          wait(&we, 1);
        }
        else
          break;
      }
      else {
        //dbg("FD %d sent %ld bytes\n", fd, bytes_sent);
        total_bytes_sent += bytes_sent;
        if (total_bytes_sent == bytes_to_send)
          return true;
      }
    }
    return false;
  }
}

// -----------------------------------------------
// port and vport are in host format
void TNetAddress::from(int port, unsigned ip4_in_host_format) {
  assert(port > 0 && port < 65535);
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(ip4_in_host_format);
  addr.sin_port = htons(port);
}

void TNetAddress::setPort(unsigned short new_app_port) {
  addr.sin_port = htons(new_app_port);
}

void TNetAddress::fromAnyAddress(int port) {
  from(port, INADDR_ANY);
}

bool TNetAddress::fromStr(const char *addr_str, int port) {
  fromAnyAddress(port);
  return inet_pton(AF_INET, addr_str, &addr.sin_addr) == 1;
}

<|endoftext|>


<commit_before>// Copyright (c) 2012 Plenluno All rights reserved.

#include <assert.h>
#include <http_parser.h>
#include <uv.h>

#include "libnode/http/server.h"
#include "./server_request_impl.h"
#include "./server_response_impl.h"
#include "../net/server_impl.h"
#include "../net/socket_impl.h"

namespace libj {
namespace node {
namespace http {

class ServerImpl;

class ServerContext {
 public:
    ServerContext(
        ServerImpl* srv,
        net::SocketImpl::Ptr sock)
        : server(srv)
        , request(ServerRequestImpl::create(sock))
        , response(ServerResponseImpl::create(sock)) {
        parser.data = this;
    }

    ServerImpl* server;
    ServerRequestImpl::Ptr request;
    ServerResponseImpl::Ptr response;
    http_parser parser;
};

class ServerImpl : public Server {
 public:
    static Ptr create() {
        Ptr p(new ServerImpl());
        return p;
    }

    Boolean listen(Int port, String::CPtr hostName, Int backlog) {
        server_->on(
            net::Server::EVENT_CLOSE,
            OnServerClose::create(this));
        server_->on(
            net::Server::EVENT_LISTENING,
            OnServerListening::create());
        server_->on(
            net::Server::EVENT_CONNECTION,
            OnServerConnection::create(this));
        return server_->listen(port, hostName, backlog);
    }

    void close() {
        server_->close();
    }

 private:
    static http_parser_settings settings;

    class OnServerClose : LIBJ_JS_FUNCTION(OnServerClose)
     public:
        static Ptr create(ServerImpl* srv) {
            Ptr p(new OnServerClose(srv));
            return p;
        }

        Value operator()(JsArray::Ptr args) {
            server_->emit(EVENT_CLOSE, JsArray::create());
            return libj::Status::OK;
        }

     private:
        ServerImpl* server_;

        OnServerClose(ServerImpl* srv) : server_(srv) {}
    };

    class OnServerListening : LIBJ_JS_FUNCTION(OnServerListening)
     public:
        static Ptr create() {
            Ptr p(new OnServerListening());
            return p;
        }

        Value operator()(JsArray::Ptr args) {
            settings.on_url = ServerImpl::onUrl;
            settings.on_header_field = ServerImpl::onHeaderField;
            settings.on_header_value = ServerImpl::onHeaderValue;
            settings.on_headers_complete = ServerImpl::onHeadersComplete;
            settings.on_message_begin = ServerImpl::onMessageBegin;
            settings.on_body = ServerImpl::onBody;
            settings.on_message_complete = ServerImpl::onMessageComplete;
            return libj::Status::OK;
        }
    };

    class OnServerConnection : LIBJ_JS_FUNCTION(OnServerConnection)
     public:
        static Ptr create(ServerImpl* srv) {
            Ptr p(new OnServerConnection(srv));
            return p;
        }

        Value operator()(JsArray::Ptr args) {
            net::SocketImpl::Ptr socket = toPtr<net::SocketImpl>(args->get(0));
            assert(socket);
            ServerContext* context = new ServerContext(server_, socket);
            http_parser_init(&context->parser, HTTP_REQUEST);
            socket->on(
                net::Socket::EVENT_DATA,
                OnSocketData::create(context));
            socket->on(
                net::Socket::EVENT_CLOSE,
                OnSocketClose::create(context));
            server_->emit(EVENT_CONNECTION, args);
            return libj::Status::OK;
        }

     private:
        ServerImpl* server_;

        OnServerConnection(ServerImpl* srv) : server_(srv) {}
    };

    class OnSocketData : LIBJ_JS_FUNCTION(OnSocketData)
     public:
        static Ptr create(ServerContext* ctxt) {
            assert(ctxt);
            Ptr p(new OnSocketData(ctxt));
            return p;
        }

        Value operator()(JsArray::Ptr args) {
            http_parser* httpParser = &context_->parser;
            Buffer::Ptr buf = toPtr<Buffer>(args->get(0));
            assert(buf);
            size_t numRead = buf->length();
            size_t numParsed = http_parser_execute(
                                httpParser,
                                &settings,
                                static_cast<const char*>(buf->data()),
                                numRead);
            ServerRequestImpl::Ptr request = context_->request;
            net::SocketImpl::Ptr socket =
                LIBJ_STATIC_PTR_CAST(net::SocketImpl)(request->connection());
            if (httpParser->upgrade) {
                socket->removeAllListeners();
                JsArray::Ptr args = JsArray::create();
                args->add(request);
                args->add(socket);
                args->add(buf->slice(numParsed));
                context_->server->emit(EVENT_UPGRADE, args);
                delete context_;
            } else if (numParsed < numRead) {  // parse error
                socket->destroy(
                    libj::Error::create(libj::Error::ILLEGAL_REQUEST));
            }
            return libj::Status::OK;
        }

     private:
        ServerContext* context_;

        OnSocketData(ServerContext* ctxt) : context_(ctxt) {}
    };

    class OnSocketClose : LIBJ_JS_FUNCTION(OnSocketClose)
     public:
        static Ptr create(ServerContext* ctxt) {
            assert(ctxt);
            Ptr p(new OnSocketClose(ctxt));
            return p;
        }

        Value operator()(JsArray::Ptr args) {
            if (context_) {
                JsArray::Ptr noArgs = JsArray::create();
                context_->request->emit(ServerRequest::EVENT_CLOSE, noArgs);
                context_->response->emit(ServerResponse::EVENT_CLOSE, noArgs);
                delete context_;
                context_ = NULL;
            }
            return libj::Status::OK;
        }

     private:
        ServerContext* context_;

        OnSocketClose(ServerContext* ctxt) : context_(ctxt) {}
    };

    static int onUrl(http_parser* parser, const char* at, size_t length) {
        ServerContext* context = static_cast<ServerContext*>(parser->data);
        String::CPtr url = String::create(at, String::UTF8, length);
        context->request->setUrl(url);
        return 0;
    }

    static String::CPtr headerName;

    static int onHeaderField(
        http_parser* parser, const char* at, size_t length) {
        headerName = String::create(at, String::UTF8, length);
        return 0;
    }

    static int onHeaderValue(
        http_parser* parser, const char* at, size_t length) {
        ServerContext* context = static_cast<ServerContext*>(parser->data);
        context->request->setHeader(
            headerName,
            String::create(at, String::UTF8, length));
        return 0;
    }

    static int onHeadersComplete(http_parser* parser) {
        static const String::CPtr methodDelete = String::create("DELETE");
        static const String::CPtr methodGet = String::create("GET");
        static const String::CPtr methodHead = String::create("HEAD");
        static const String::CPtr methodPost = String::create("POST");
        static const String::CPtr methodPut = String::create("PUT");
        static const String::CPtr methodConnect = String::create("CONNECT");
        static const String::CPtr methodOptions = String::create("OPTIONS");
        static const String::CPtr methodTrace = String::create("TRACE");
        static const String::CPtr dot = String::create(".");

        ServerContext* context = static_cast<ServerContext*>(parser->data);
        assert(context);
        switch (parser->method) {
        case HTTP_DELETE:
            context->request->setMethod(methodDelete);
            break;
        case HTTP_GET:
            context->request->setMethod(methodGet);
            break;
        case HTTP_HEAD:
            context->request->setMethod(methodHead);
            break;
        case HTTP_POST:
            context->request->setMethod(methodPost);
            break;
        case HTTP_PUT:
            context->request->setMethod(methodPut);
            break;
        case HTTP_CONNECT:
            context->request->setMethod(methodConnect);
            break;
        case HTTP_OPTIONS:
            context->request->setMethod(methodOptions);
            break;
        case HTTP_TRACE:
            context->request->setMethod(methodTrace);
            break;
        default:
            context->request->setMethod(String::create());
        }

        Int majorVer = static_cast<Int>(parser->http_major);
        Int minorVer = static_cast<Int>(parser->http_minor);
        String::CPtr httpVer = String::valueOf(majorVer);
        httpVer = httpVer->concat(dot);
        httpVer = httpVer->concat(String::valueOf(minorVer));
        context->request->setHttpVersion(httpVer);

        JsArray::Ptr args = JsArray::create();
        ServerRequest::Ptr req(context->request);
        ServerResponse::Ptr res(context->response);
        args->add(req);
        args->add(res);
        assert(context->server);
        context->server->emit(Server::EVENT_REQUEST, args);
        return 0;
    }

    static int onMessageBegin(http_parser* parser) {
        return 0;
    }

    static int onBody(http_parser* parser, const char* at, size_t length) {
        ServerContext* context = static_cast<ServerContext*>(parser->data);
        ServerRequestImpl::Ptr request = context->request;
        if (request) {
            Buffer::Ptr chunk = Buffer::create(at, length);
            JsArray::Ptr args = JsArray::create();
            if (request->hasEncoding()) {
                Buffer::Encoding enc = request->getEncoding();
                args->add(chunk->toString(enc));
            } else {
                args->add(chunk);
            }
            context->request->emit(ServerRequest::EVENT_DATA, args);
        }
        return 0;
    }

    static int onMessageComplete(http_parser* parser) {
        ServerContext* context = static_cast<ServerContext*>(parser->data);
        if (context->request) {
            JsArray::Ptr args = JsArray::create();
            context->request->emit(ServerRequest::EVENT_END, args);
        }
        return 0;
    }

 private:
    EventEmitter::Ptr ee_;
    net::ServerImpl::Ptr server_;

    ServerImpl()
        : ee_(EventEmitter::create())
        , server_(net::Server::create()) {}

    LIBNODE_EVENT_EMITTER_IMPL(ee_);
};

http_parser_settings ServerImpl::settings = {};
String::CPtr ServerImpl::headerName = String::null();

const String::CPtr Server::IN_ADDR_ANY = String::create("0.0.0.0");
const String::CPtr Server::EVENT_REQUEST = String::create("request");
const String::CPtr Server::EVENT_CONNECTION = String::create("connection");
const String::CPtr Server::EVENT_CLOSE = String::create("close");
const String::CPtr Server::EVENT_UPGRADE = String::create("upgrade");

Server::Ptr Server::create() {
    return ServerImpl::create();
}

}  // namespace http
}  // namespace node
}  // namespace libj
<commit_msg>refactor ServerImpl<commit_after>// Copyright (c) 2012 Plenluno All rights reserved.

#include <assert.h>
#include <http_parser.h>
#include <uv.h>

#include "libnode/http/server.h"
#include "./server_request_impl.h"
#include "./server_response_impl.h"
#include "../net/server_impl.h"
#include "../net/socket_impl.h"

namespace libj {
namespace node {
namespace http {

class ServerImpl;

class ServerContext {
 public:
    ServerContext(
        ServerImpl* srv,
        net::SocketImpl::Ptr sock)
        : server(srv)
        , request(ServerRequestImpl::create(sock))
        , response(ServerResponseImpl::create(sock)) {
        parser.data = this;
    }

    ServerImpl* server;
    ServerRequestImpl::Ptr request;
    ServerResponseImpl::Ptr response;
    http_parser parser;
};

class ServerImpl : public Server {
 public:
    static Ptr create() {
        Ptr p(new ServerImpl());
        return p;
    }

    Boolean listen(Int port, String::CPtr hostName, Int backlog) {
        server_->on(
            net::Server::EVENT_CLOSE,
            OnServerClose::create(this));
        server_->on(
            net::Server::EVENT_LISTENING,
            OnServerListening::create());
        server_->on(
            net::Server::EVENT_CONNECTION,
            OnServerConnection::create(this));
        return server_->listen(port, hostName, backlog);
    }

    void close() {
        server_->close();
    }

 private:
    static http_parser_settings settings;

    class OnServerClose : LIBJ_JS_FUNCTION(OnServerClose)
     public:
        static Ptr create(ServerImpl* srv) {
            Ptr p(new OnServerClose(srv));
            return p;
        }

        Value operator()(JsArray::Ptr args) {
            server_->emit(EVENT_CLOSE, JsArray::create());
            return libj::Status::OK;
        }

     private:
        ServerImpl* server_;

        OnServerClose(ServerImpl* srv) : server_(srv) {}
    };

    class OnServerListening : LIBJ_JS_FUNCTION(OnServerListening)
     public:
        static Ptr create() {
            Ptr p(new OnServerListening());
            return p;
        }

        Value operator()(JsArray::Ptr args) {
            settings.on_url = ServerImpl::onUrl;
            settings.on_header_field = ServerImpl::onHeaderField;
            settings.on_header_value = ServerImpl::onHeaderValue;
            settings.on_headers_complete = ServerImpl::onHeadersComplete;
            settings.on_message_begin = ServerImpl::onMessageBegin;
            settings.on_body = ServerImpl::onBody;
            settings.on_message_complete = ServerImpl::onMessageComplete;
            return libj::Status::OK;
        }
    };

    class OnServerConnection : LIBJ_JS_FUNCTION(OnServerConnection)
     public:
        static Ptr create(ServerImpl* srv) {
            Ptr p(new OnServerConnection(srv));
            return p;
        }

        Value operator()(JsArray::Ptr args) {
            net::SocketImpl::Ptr socket = toPtr<net::SocketImpl>(args->get(0));
            assert(socket);
            ServerContext* context = new ServerContext(server_, socket);
            http_parser_init(&context->parser, HTTP_REQUEST);
            socket->on(
                net::Socket::EVENT_DATA,
                OnSocketData::create(context));
            socket->on(
                net::Socket::EVENT_CLOSE,
                OnSocketClose::create(context));
            server_->emit(EVENT_CONNECTION, args);
            return libj::Status::OK;
        }

     private:
        ServerImpl* server_;

        OnServerConnection(ServerImpl* srv) : server_(srv) {}
    };

    class OnSocketData : LIBJ_JS_FUNCTION(OnSocketData)
     public:
        static Ptr create(ServerContext* ctxt) {
            assert(ctxt);
            Ptr p(new OnSocketData(ctxt));
            return p;
        }

        Value operator()(JsArray::Ptr args) {
            http_parser* httpParser = &context_->parser;
            Buffer::Ptr buf = toPtr<Buffer>(args->get(0));
            assert(buf);
            size_t numRead = buf->length();
            size_t numParsed = http_parser_execute(
                                httpParser,
                                &settings,
                                static_cast<const char*>(buf->data()),
                                numRead);
            ServerRequestImpl::Ptr request = context_->request;
            net::SocketImpl::Ptr socket =
                LIBJ_STATIC_PTR_CAST(net::SocketImpl)(request->connection());
            if (httpParser->upgrade) {
                socket->removeAllListeners();
                JsArray::Ptr args = JsArray::create();
                args->add(request);
                args->add(socket);
                args->add(buf->slice(numParsed));
                context_->server->emit(EVENT_UPGRADE, args);
                delete context_;
            } else if (numParsed < numRead) {  // parse error
                socket->destroy(
                    libj::Error::create(libj::Error::ILLEGAL_REQUEST));
            }
            return libj::Status::OK;
        }

     private:
        ServerContext* context_;

        OnSocketData(ServerContext* ctxt) : context_(ctxt) {}
    };

    class OnSocketClose : LIBJ_JS_FUNCTION(OnSocketClose)
     public:
        static Ptr create(ServerContext* ctxt) {
            assert(ctxt);
            Ptr p(new OnSocketClose(ctxt));
            return p;
        }

        Value operator()(JsArray::Ptr args) {
            if (context_) {
                JsArray::Ptr noArgs = JsArray::create();
                context_->request->emit(ServerRequest::EVENT_CLOSE, noArgs);
                context_->response->emit(ServerResponse::EVENT_CLOSE, noArgs);
                delete context_;
                context_ = NULL;
            }
            return libj::Status::OK;
        }

     private:
        ServerContext* context_;

        OnSocketClose(ServerContext* ctxt) : context_(ctxt) {}
    };

    static int onUrl(http_parser* parser, const char* at, size_t length) {
        ServerContext* context = static_cast<ServerContext*>(parser->data);
        String::CPtr url = String::create(at, String::UTF8, length);
        context->request->setUrl(url);
        return 0;
    }

    static String::CPtr headerName;

    static int onHeaderField(
        http_parser* parser, const char* at, size_t length) {
        headerName = String::create(at, String::UTF8, length);
        return 0;
    }

    static int onHeaderValue(
        http_parser* parser, const char* at, size_t length) {
        ServerContext* context = static_cast<ServerContext*>(parser->data);
        context->request->setHeader(
            headerName,
            String::create(at, String::UTF8, length));
        return 0;
    }

    static int onHeadersComplete(http_parser* parser) {
        static const String::CPtr methodDelete = String::create("DELETE");
        static const String::CPtr methodGet = String::create("GET");
        static const String::CPtr methodHead = String::create("HEAD");
        static const String::CPtr methodPost = String::create("POST");
        static const String::CPtr methodPut = String::create("PUT");
        static const String::CPtr methodConnect = String::create("CONNECT");
        static const String::CPtr methodOptions = String::create("OPTIONS");
        static const String::CPtr methodTrace = String::create("TRACE");
        static const String::CPtr dot = String::create(".");

        ServerContext* context = static_cast<ServerContext*>(parser->data);
        assert(context);
        switch (parser->method) {
        case HTTP_DELETE:
            context->request->setMethod(methodDelete);
            break;
        case HTTP_GET:
            context->request->setMethod(methodGet);
            break;
        case HTTP_HEAD:
            context->request->setMethod(methodHead);
            break;
        case HTTP_POST:
            context->request->setMethod(methodPost);
            break;
        case HTTP_PUT:
            context->request->setMethod(methodPut);
            break;
        case HTTP_CONNECT:
            context->request->setMethod(methodConnect);
            break;
        case HTTP_OPTIONS:
            context->request->setMethod(methodOptions);
            break;
        case HTTP_TRACE:
            context->request->setMethod(methodTrace);
            break;
        default:
            context->request->setMethod(String::create());
        }

        Int majorVer = static_cast<Int>(parser->http_major);
        Int minorVer = static_cast<Int>(parser->http_minor);
        String::CPtr httpVer = String::valueOf(majorVer);
        httpVer = httpVer->concat(dot);
        httpVer = httpVer->concat(String::valueOf(minorVer));
        context->request->setHttpVersion(httpVer);

        JsArray::Ptr args = JsArray::create();
        ServerRequest::Ptr req(context->request);
        ServerResponse::Ptr res(context->response);
        args->add(req);
        args->add(res);
        assert(context->server);
        context->server->emit(Server::EVENT_REQUEST, args);
        return 0;
    }

    static int onMessageBegin(http_parser* parser) {
        return 0;
    }

    static int onBody(http_parser* parser, const char* at, size_t length) {
        ServerContext* context = static_cast<ServerContext*>(parser->data);
        ServerRequestImpl::Ptr request = context->request;
        if (request) {
            Buffer::Ptr chunk = Buffer::create(at, length);
            JsArray::Ptr args = JsArray::create();
            if (request->hasEncoding()) {
                Buffer::Encoding enc = request->getEncoding();
                args->add(chunk->toString(enc));
            } else {
                args->add(chunk);
            }
            request->emit(ServerRequest::EVENT_DATA, args);
        }
        return 0;
    }

    static int onMessageComplete(http_parser* parser) {
        ServerContext* context = static_cast<ServerContext*>(parser->data);
        ServerRequestImpl::Ptr request = context->request;
        if (request) {
            JsArray::Ptr args = JsArray::create();
            request->emit(ServerRequest::EVENT_END, args);
        }
        return 0;
    }

 private:
    EventEmitter::Ptr ee_;
    net::ServerImpl::Ptr server_;

    ServerImpl()
        : ee_(EventEmitter::create())
        , server_(net::Server::create()) {}

    LIBNODE_EVENT_EMITTER_IMPL(ee_);
};

http_parser_settings ServerImpl::settings = {};
String::CPtr ServerImpl::headerName = String::null();

const String::CPtr Server::IN_ADDR_ANY = String::create("0.0.0.0");
const String::CPtr Server::EVENT_REQUEST = String::create("request");
const String::CPtr Server::EVENT_CONNECTION = String::create("connection");
const String::CPtr Server::EVENT_CLOSE = String::create("close");
const String::CPtr Server::EVENT_UPGRADE = String::create("upgrade");

Server::Ptr Server::create() {
    return ServerImpl::create();
}

}  // namespace http
}  // namespace node
}  // namespace libj
<|endoftext|>