<commit_before>// mgpathsp.cpp: 实现路径图形 MgPathShape
// Copyright (c) 2004-2014, Zhang Yungui
// License: LGPL, https://github.com/touchvg/vgcore

#include "mgpathsp.h"
#include "mgshape_.h"
#include <sstream>

MG_IMPLEMENT_CREATE(MgPathShape)

MgPathShape::MgPathShape()
{
}

MgPathShape::~MgPathShape()
{
}

int MgPathShape::_getPointCount() const
{
    return _path.getCount();
}

Point2d MgPathShape::_getPoint(int index) const
{
    return _path.getPoint(index);
}

void MgPathShape::_setPoint(int index, const Point2d& pt)
{
    _path.setPoint(index, pt);
}

void MgPathShape::_copy(const MgPathShape& src)
{
    _path.copy(src._path);
    __super::_copy(src);
}

bool MgPathShape::_equals(const MgPathShape& src) const
{
    if (_path.getCount() != src._path.getCount() || !__super::_equals(src)) {
        return false;
    }
    for (int i = 0; i < _path.getCount(); i++) {
        if (_path.getPoint(i) != src._path.getPoint(i) ||
            _path.getNodeType(i) != src._path.getNodeType(i)) {
            return false;
        }
    }
    return true;
}

void MgPathShape::_update()
{
    _extent.set(_path.getCount(), _path.getPoints());
    __super::_update();
}

void MgPathShape::_transform(const Matrix2d& mat)
{
    for (int i = 0; i < _path.getCount(); i++) {
        _path.setPoint(i, _path.getPoint(i) * mat);
    }
    __super::_transform(mat);
}

void MgPathShape::_clear()
{
    _path.clear();
    __super::_clear();
}

bool MgPathShape::_isClosed() const
{
    return !!(_path.getNodeType(_path.getCount() - 1) & kGiCloseFigure);
}

bool MgPathShape::isCurve() const
{
    for (int i = 0; i < _path.getCount(); i++) {
        if (_path.getNodeType(i) & (kGiBezierTo | kGiQuadTo))
            return true;
    }
    return false;
}

float MgPathShape::_hitTest(const Point2d& pt, float tol, MgHitResult& res) const
{
    int n = _path.getCount();
    const Point2d* pts = _path.getPoints();
    const char* types = _path.getTypes();
    Point2d pos, ends, bz[7], nearpt;
    bool err = false;
    const Box2d rect (pt, 2 * tol, 2 * tol);

    res.dist = _FLT_MAX - tol;
    for (int i = 0; i < n && !err; i++) {
        float dist = _FLT_MAX;
        pos = ends;

        switch (types[i] & ~kGiCloseFigure) {
            case kGiMoveTo:
                ends = pts[i];
                break;

            case kGiLineTo:
                ends = pts[i];
                if (rect.isIntersect(Box2d(pos, ends))) {
                    dist = mglnrel::ptToLine(pos, ends, pt, nearpt);
                }
                break;

            case kGiBezierTo:
                if (i + 2 >= n) {
                    err = true;
                    break;
                }
                bz[0] = pos;
                bz[1] = pts[i];
                bz[2] = pts[i+1];
                bz[3] = pts[i+2];
                ends = bz[3];

                if (rect.isIntersect(mgnear::bezierBox1(bz))) {
                    dist = mgnear::nearestOnBezier(pt, bz, nearpt);
                }
                i += 2;
                break;

            case kGiQuadTo:
                if (i + 1 >= n) {
                    err = true;
                    break;
                }
                bz[0] = pos;
                bz[1] = pts[i];
                bz[2] = pts[i+1];
                ends = bz[2];

                mgcurv::quadBezierToCubic(bz, bz + 3);
                if (rect.isIntersect(mgnear::bezierBox1(bz + 3))) {
                    dist = mgnear::nearestOnBezier(pt, bz, nearpt);
                }
                i++;
                break;

            default:
                err = true;
                break;
        }
        if (res.dist > dist) {
            res.dist = dist;
            res.segment = i;
        }
    }

    return res.dist;
}

bool MgPathShape::_hitTestBox(const Box2d& rect) const
{
    if (!__super::_hitTestBox(rect))
        return false;

    int n = _path.getCount();
    const Point2d* pts = _path.getPoints();
    const char* types = _path.getTypes();
    Point2d pos, ends, bz[7];
    int ret = 0;

    for (int i = 0; i < n && !ret; i++) {
        pos = ends;

        switch (types[i] & ~kGiCloseFigure) {
            case kGiMoveTo:
                ends = pts[i];
                break;

            case kGiLineTo:
                ends = pts[i];
                ret = rect.isIntersect(Box2d(pos, ends));
                break;

            case kGiBezierTo:
                if (i + 2 >= n) {
                    ret |= 2;
                    break;
                }
                bz[0] = pos;
                bz[1] = pts[i];
                bz[2] = pts[i+1];
                bz[3] = pts[i+2];
                ends = bz[3];
                ret |= rect.isIntersect(mgnear::bezierBox1(bz)) ? 1 : 0;
                i += 2;
                break;

            case kGiQuadTo:
                if (i + 1 >= n) {
                    ret |= 2;
                    break;
                }
                bz[0] = pos;
                bz[1] = pts[i];
                bz[2] = pts[i+1];
                ends = bz[2];
                mgcurv::quadBezierToCubic(bz, bz + 3);
                ret |= rect.isIntersect(mgnear::bezierBox1(bz + 3)) ? 1 : 0;
                i++;
                break;

            default:
                ret |= 2;
                break;
        }
    }

    return ret == 1;
}

bool MgPathShape::_draw(int mode, GiGraphics& gs, const GiContext& ctx, int segment) const
{
    bool ret = gs.drawPath(&ctx, _path, mode == 0);
    return __super::_draw(mode, gs, ctx, segment) || ret;
}

bool MgPathShape::_save(MgStorage* s) const
{
    bool ret = __super::_save(s);
    std::stringstream ss;

    int n = _path.getCount();
    const Point2d* pts = _path.getPoints();
    const char* types = _path.getTypes();
    char cmd = 0;

    for (int i = 0; i < n; i++) {
        switch (types[i] & ~kGiCloseFigure) {
            case kGiMoveTo:
                ss << "M" << pts[i].x << " " << pts[i].y << " ";
                cmd = 'L';
                break;

            case kGiLineTo:
                if (cmd != 'L') {
                    cmd = 'L';
                    ss << cmd;
                }
                ss << pts[i].x << " " << pts[i].y << " ";
                break;

            case kGiBezierTo:
                if (i + 2 >= n) {
                    break;
                }
                cmd = 'C';
                ss << cmd << pts[i].x << " " << pts[i].y;
                ss << " " << pts[i+1].x << " " << pts[i+1].y;
                ss << " " << pts[i+2].x << " " << pts[i+2].y << " ";
                i += 2;
                break;

            case kGiQuadTo:
                if (i + 1 >= n) {
                    break;
                }
                cmd = 'Q';
                ss << cmd << pts[i].x << " " << pts[i].y;
                ss << " " << pts[i+1].x << " " << pts[i+1].y << " ";
                i++;
                break;

            default:
                break;
        }
        if (types[i] & kGiCloseFigure) {
            cmd = 'Z';
            ss << cmd;
        }
    }

    s->writeString("d", ss.str().c_str());

    return ret;
}

static void parsePath(const char* s, GiPath& path);

bool MgPathShape::_load(MgShapeFactory* factory, MgStorage* s)
{
    bool ret = __super::_load(factory, s);
    int len = s->readString("d", NULL, 0);

    if (!ret || len < 1)
        return false;

    char *buf = new char[1 + len];

    len = s->readString("d", buf, len);
    buf[len] = 0;
    ret = importSVGPath(buf);

    delete[] buf;

    return ret;
}

bool MgPathShape::importSVGPath(const char* d)
{
    _path.clear();
    parsePath(d, _path);
    return true;
}

// parsePath() is based on nanosvg.h (https://github.com/memononen/nanosvg)
// NanoSVG is a simple stupid single-header-file SVG parse.
// Copyright (c) 2013-14 Mikko Mononen memon@inside.org

static int nsvg__isspace(char c)
{
	return strchr(" \t\n\v\f\r", c) != 0;
}

static int nsvg__isdigit(char c)
{
	return strchr("0123456789", c) != 0;
}

static int nsvg__isnum(char c)
{
	return strchr("0123456789+-.eE", c) != 0;
}

static const char* nsvg__getNextPathItem(const char* s, char* it)
{
	int i = 0;
	it[0] = '\0';
	// Skip white spaces and commas
	while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
	if (!*s) return s;
	if (*s == '-' || *s == '+' || nsvg__isdigit(*s)) {
		// sign
		if (*s == '-' || *s == '+') {
			if (i < 63) it[i++] = *s;
			s++;
		}
		// integer part
		while (*s && nsvg__isdigit(*s)) {
			if (i < 63) it[i++] = *s;
			s++;
		}
		if (*s == '.') {
			// decimal point
			if (i < 63) it[i++] = *s;
			s++;
			// fraction part
			while (*s && nsvg__isdigit(*s)) {
				if (i < 63) it[i++] = *s;
				s++;
			}
		}
		// exponent
		if (*s == 'e' || *s == 'E') {
			if (i < 63) it[i++] = *s;
			s++;
			if (*s == '-' || *s == '+') {
				if (i < 63) it[i++] = *s;
				s++;
			}
			while (*s && nsvg__isdigit(*s)) {
				if (i < 63) it[i++] = *s;
				s++;
			}
		}
		it[i] = '\0';
	} else {
		// Parse command
		it[0] = *s++;
		it[1] = '\0';
		return s;
	}

	return s;
}

static int nsvg__getArgsPerElement(char cmd)
{
	switch (cmd) {
		case 'v':
		case 'V':
		case 'h':
		case 'H':
			return 1;
		case 'm':
		case 'M':
		case 'l':
		case 'L':
		case 't':
		case 'T':
			return 2;
		case 'q':
		case 'Q':
		case 's':
		case 'S':
			return 4;
		case 'c':
		case 'C':
			return 6;
		case 'a':
		case 'A':
			return 7;
	}
	return 0;
}

static void parsePath(const char* s, GiPath& path)
{
    char item[64];
    char cmd = 0;
	float args[10];
	int nargs = 0;
	int rargs = 0;

    while (*s) {
        s = nsvg__getNextPathItem(s, item);
        if (!*item) break;
        if (!nsvg__isnum(item[0])) {
            cmd = item[0];
            rargs = nsvg__getArgsPerElement(cmd);
            nargs = 0;
        } else {
            if (nargs < 10)
                args[nargs++] = (float)atof(item);
            if (nargs >= rargs) {
                switch (cmd) {
                    case 'm':
                    case 'M':
                        path.moveTo(Point2d(args[0], args[1]));
                        // Moveto can be followed by multiple coordinate pairs,
                        // which should be treated as linetos.
                        cmd = (cmd == 'm') ? 'l' : 'L';
                        rargs = nsvg__getArgsPerElement(cmd);
                        break;
                    case 'l':
                    case 'L':
                        path.lineTo(Point2d(args[0], args[1]));
                        break;
                    case 'H':
                    case 'h':
                        break;
                    case 'V':
                    case 'v':
                        break;
                    case 'C':
                    case 'c':
                        path.bezierTo(Point2d(args[0], args[1]),
                                      Point2d(args[2], args[3]),
                                      Point2d(args[4], args[5]));
                        break;
                    case 'S':
                    case 's':
                        break;
                    case 'Q':
                    case 'q':
                        path.quadTo(Point2d(args[0], args[1]),
                                    Point2d(args[2], args[3]));
                        break;
                    case 'T':
                    case 't':
                        break;
                    case 'A':
                    case 'a':
                        path.arcTo(Point2d(args[0], args[1]));
                        break;
                    default:
                        break;
                }
                nargs = 0;
            }
        }
    }
}
<commit_msg>Add include for Android.<commit_after>// mgpathsp.cpp: 实现路径图形 MgPathShape
// Copyright (c) 2004-2014, Zhang Yungui
// License: LGPL, https://github.com/touchvg/vgcore

#include "mgpathsp.h"
#include "mgshape_.h"
#include <sstream>
#include <stdlib.h>

MG_IMPLEMENT_CREATE(MgPathShape)

MgPathShape::MgPathShape()
{
}

MgPathShape::~MgPathShape()
{
}

int MgPathShape::_getPointCount() const
{
    return _path.getCount();
}

Point2d MgPathShape::_getPoint(int index) const
{
    return _path.getPoint(index);
}

void MgPathShape::_setPoint(int index, const Point2d& pt)
{
    _path.setPoint(index, pt);
}

void MgPathShape::_copy(const MgPathShape& src)
{
    _path.copy(src._path);
    __super::_copy(src);
}

bool MgPathShape::_equals(const MgPathShape& src) const
{
    if (_path.getCount() != src._path.getCount() || !__super::_equals(src)) {
        return false;
    }
    for (int i = 0; i < _path.getCount(); i++) {
        if (_path.getPoint(i) != src._path.getPoint(i) ||
            _path.getNodeType(i) != src._path.getNodeType(i)) {
            return false;
        }
    }
    return true;
}

void MgPathShape::_update()
{
    _extent.set(_path.getCount(), _path.getPoints());
    __super::_update();
}

void MgPathShape::_transform(const Matrix2d& mat)
{
    for (int i = 0; i < _path.getCount(); i++) {
        _path.setPoint(i, _path.getPoint(i) * mat);
    }
    __super::_transform(mat);
}

void MgPathShape::_clear()
{
    _path.clear();
    __super::_clear();
}

bool MgPathShape::_isClosed() const
{
    return !!(_path.getNodeType(_path.getCount() - 1) & kGiCloseFigure);
}

bool MgPathShape::isCurve() const
{
    for (int i = 0; i < _path.getCount(); i++) {
        if (_path.getNodeType(i) & (kGiBezierTo | kGiQuadTo))
            return true;
    }
    return false;
}

float MgPathShape::_hitTest(const Point2d& pt, float tol, MgHitResult& res) const
{
    int n = _path.getCount();
    const Point2d* pts = _path.getPoints();
    const char* types = _path.getTypes();
    Point2d pos, ends, bz[7], nearpt;
    bool err = false;
    const Box2d rect (pt, 2 * tol, 2 * tol);

    res.dist = _FLT_MAX - tol;
    for (int i = 0; i < n && !err; i++) {
        float dist = _FLT_MAX;
        pos = ends;

        switch (types[i] & ~kGiCloseFigure) {
            case kGiMoveTo:
                ends = pts[i];
                break;

            case kGiLineTo:
                ends = pts[i];
                if (rect.isIntersect(Box2d(pos, ends))) {
                    dist = mglnrel::ptToLine(pos, ends, pt, nearpt);
                }
                break;

            case kGiBezierTo:
                if (i + 2 >= n) {
                    err = true;
                    break;
                }
                bz[0] = pos;
                bz[1] = pts[i];
                bz[2] = pts[i+1];
                bz[3] = pts[i+2];
                ends = bz[3];

                if (rect.isIntersect(mgnear::bezierBox1(bz))) {
                    dist = mgnear::nearestOnBezier(pt, bz, nearpt);
                }
                i += 2;
                break;

            case kGiQuadTo:
                if (i + 1 >= n) {
                    err = true;
                    break;
                }
                bz[0] = pos;
                bz[1] = pts[i];
                bz[2] = pts[i+1];
                ends = bz[2];

                mgcurv::quadBezierToCubic(bz, bz + 3);
                if (rect.isIntersect(mgnear::bezierBox1(bz + 3))) {
                    dist = mgnear::nearestOnBezier(pt, bz, nearpt);
                }
                i++;
                break;

            default:
                err = true;
                break;
        }
        if (res.dist > dist) {
            res.dist = dist;
            res.segment = i;
        }
    }

    return res.dist;
}

bool MgPathShape::_hitTestBox(const Box2d& rect) const
{
    if (!__super::_hitTestBox(rect))
        return false;

    int n = _path.getCount();
    const Point2d* pts = _path.getPoints();
    const char* types = _path.getTypes();
    Point2d pos, ends, bz[7];
    int ret = 0;

    for (int i = 0; i < n && !ret; i++) {
        pos = ends;

        switch (types[i] & ~kGiCloseFigure) {
            case kGiMoveTo:
                ends = pts[i];
                break;

            case kGiLineTo:
                ends = pts[i];
                ret = rect.isIntersect(Box2d(pos, ends));
                break;

            case kGiBezierTo:
                if (i + 2 >= n) {
                    ret |= 2;
                    break;
                }
                bz[0] = pos;
                bz[1] = pts[i];
                bz[2] = pts[i+1];
                bz[3] = pts[i+2];
                ends = bz[3];
                ret |= rect.isIntersect(mgnear::bezierBox1(bz)) ? 1 : 0;
                i += 2;
                break;

            case kGiQuadTo:
                if (i + 1 >= n) {
                    ret |= 2;
                    break;
                }
                bz[0] = pos;
                bz[1] = pts[i];
                bz[2] = pts[i+1];
                ends = bz[2];
                mgcurv::quadBezierToCubic(bz, bz + 3);
                ret |= rect.isIntersect(mgnear::bezierBox1(bz + 3)) ? 1 : 0;
                i++;
                break;

            default:
                ret |= 2;
                break;
        }
    }

    return ret == 1;
}

bool MgPathShape::_draw(int mode, GiGraphics& gs, const GiContext& ctx, int segment) const
{
    bool ret = gs.drawPath(&ctx, _path, mode == 0);
    return __super::_draw(mode, gs, ctx, segment) || ret;
}

bool MgPathShape::_save(MgStorage* s) const
{
    bool ret = __super::_save(s);
    std::stringstream ss;

    int n = _path.getCount();
    const Point2d* pts = _path.getPoints();
    const char* types = _path.getTypes();
    char cmd = 0;

    for (int i = 0; i < n; i++) {
        switch (types[i] & ~kGiCloseFigure) {
            case kGiMoveTo:
                ss << "M" << pts[i].x << " " << pts[i].y << " ";
                cmd = 'L';
                break;

            case kGiLineTo:
                if (cmd != 'L') {
                    cmd = 'L';
                    ss << cmd;
                }
                ss << pts[i].x << " " << pts[i].y << " ";
                break;

            case kGiBezierTo:
                if (i + 2 >= n) {
                    break;
                }
                cmd = 'C';
                ss << cmd << pts[i].x << " " << pts[i].y;
                ss << " " << pts[i+1].x << " " << pts[i+1].y;
                ss << " " << pts[i+2].x << " " << pts[i+2].y << " ";
                i += 2;
                break;

            case kGiQuadTo:
                if (i + 1 >= n) {
                    break;
                }
                cmd = 'Q';
                ss << cmd << pts[i].x << " " << pts[i].y;
                ss << " " << pts[i+1].x << " " << pts[i+1].y << " ";
                i++;
                break;

            default:
                break;
        }
        if (types[i] & kGiCloseFigure) {
            cmd = 'Z';
            ss << cmd;
        }
    }

    s->writeString("d", ss.str().c_str());

    return ret;
}

static void parsePath(const char* s, GiPath& path);

bool MgPathShape::_load(MgShapeFactory* factory, MgStorage* s)
{
    bool ret = __super::_load(factory, s);
    int len = s->readString("d", NULL, 0);

    if (!ret || len < 1)
        return false;

    char *buf = new char[1 + len];

    len = s->readString("d", buf, len);
    buf[len] = 0;
    ret = importSVGPath(buf);

    delete[] buf;

    return ret;
}

bool MgPathShape::importSVGPath(const char* d)
{
    _path.clear();
    parsePath(d, _path);
    return true;
}

// parsePath() is based on nanosvg.h (https://github.com/memononen/nanosvg)
// NanoSVG is a simple stupid single-header-file SVG parse.
// Copyright (c) 2013-14 Mikko Mononen memon@inside.org

static int nsvg__isspace(char c)
{
	return strchr(" \t\n\v\f\r", c) != 0;
}

static int nsvg__isdigit(char c)
{
	return strchr("0123456789", c) != 0;
}

static int nsvg__isnum(char c)
{
	return strchr("0123456789+-.eE", c) != 0;
}

static const char* nsvg__getNextPathItem(const char* s, char* it)
{
	int i = 0;
	it[0] = '\0';
	// Skip white spaces and commas
	while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
	if (!*s) return s;
	if (*s == '-' || *s == '+' || nsvg__isdigit(*s)) {
		// sign
		if (*s == '-' || *s == '+') {
			if (i < 63) it[i++] = *s;
			s++;
		}
		// integer part
		while (*s && nsvg__isdigit(*s)) {
			if (i < 63) it[i++] = *s;
			s++;
		}
		if (*s == '.') {
			// decimal point
			if (i < 63) it[i++] = *s;
			s++;
			// fraction part
			while (*s && nsvg__isdigit(*s)) {
				if (i < 63) it[i++] = *s;
				s++;
			}
		}
		// exponent
		if (*s == 'e' || *s == 'E') {
			if (i < 63) it[i++] = *s;
			s++;
			if (*s == '-' || *s == '+') {
				if (i < 63) it[i++] = *s;
				s++;
			}
			while (*s && nsvg__isdigit(*s)) {
				if (i < 63) it[i++] = *s;
				s++;
			}
		}
		it[i] = '\0';
	} else {
		// Parse command
		it[0] = *s++;
		it[1] = '\0';
		return s;
	}

	return s;
}

static int nsvg__getArgsPerElement(char cmd)
{
	switch (cmd) {
		case 'v':
		case 'V':
		case 'h':
		case 'H':
			return 1;
		case 'm':
		case 'M':
		case 'l':
		case 'L':
		case 't':
		case 'T':
			return 2;
		case 'q':
		case 'Q':
		case 's':
		case 'S':
			return 4;
		case 'c':
		case 'C':
			return 6;
		case 'a':
		case 'A':
			return 7;
	}
	return 0;
}

static void parsePath(const char* s, GiPath& path)
{
    char item[64];
    char cmd = 0;
	float args[10];
	int nargs = 0;
	int rargs = 0;

    while (*s) {
        s = nsvg__getNextPathItem(s, item);
        if (!*item) break;
        if (!nsvg__isnum(item[0])) {
            cmd = item[0];
            rargs = nsvg__getArgsPerElement(cmd);
            nargs = 0;
        } else {
            if (nargs < 10)
                args[nargs++] = (float)atof(item);
            if (nargs >= rargs) {
                switch (cmd) {
                    case 'm':
                    case 'M':
                        path.moveTo(Point2d(args[0], args[1]));
                        // Moveto can be followed by multiple coordinate pairs,
                        // which should be treated as linetos.
                        cmd = (cmd == 'm') ? 'l' : 'L';
                        rargs = nsvg__getArgsPerElement(cmd);
                        break;
                    case 'l':
                    case 'L':
                        path.lineTo(Point2d(args[0], args[1]));
                        break;
                    case 'H':
                    case 'h':
                        break;
                    case 'V':
                    case 'v':
                        break;
                    case 'C':
                    case 'c':
                        path.bezierTo(Point2d(args[0], args[1]),
                                      Point2d(args[2], args[3]),
                                      Point2d(args[4], args[5]));
                        break;
                    case 'S':
                    case 's':
                        break;
                    case 'Q':
                    case 'q':
                        path.quadTo(Point2d(args[0], args[1]),
                                    Point2d(args[2], args[3]));
                        break;
                    case 'T':
                    case 't':
                        break;
                    case 'A':
                    case 'a':
                        path.arcTo(Point2d(args[0], args[1]));
                        break;
                    default:
                        break;
                }
                nargs = 0;
            }
        }
    }
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: lateinitthread.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: hr $ $Date: 2004-07-23 11:12:10 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include "lateinitthread.hxx"

//_______________________________________________
// includes

//_______________________________________________
// namespace

namespace filter{
    namespace config{

namespace css = ::com::sun::star;

//_______________________________________________
// definitions

/*-----------------------------------------------
    14.08.2003 09:31
-----------------------------------------------*/
LateInitThread::LateInitThread()
{
}

/*-----------------------------------------------
    14.08.2003 08:42
-----------------------------------------------*/
LateInitThread::~LateInitThread()
{
}

/*-----------------------------------------------
    28.10.2003 09:30
-----------------------------------------------*/
void SAL_CALL LateInitThread::run()
{
    // sal_True => It indicates using of this method by this thread
    // The filter cache use this information to show an assertion
    // for "optimization failure" in case the first calli of loadAll()
    // was not this thread ...

    // Further please dont catch any exception here.
    // May be they show the problem of a corrupted filter
    // configuration, which is handled inside our event loop or desktop.main()!

    ::salhelper::SingletonRef< FilterCache > rCache;
    rCache->load(FilterCache::E_CONTAINS_ALL, sal_True);
}

    } // namespace config
} // namespace filter
<commit_msg>INTEGRATION: CWS ooo19126 (1.4.254); FILE MERGED 2005/09/05 14:30:24 rt 1.4.254.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: lateinitthread.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 21:30:34 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include "lateinitthread.hxx"

//_______________________________________________
// includes

//_______________________________________________
// namespace

namespace filter{
    namespace config{

namespace css = ::com::sun::star;

//_______________________________________________
// definitions

/*-----------------------------------------------
    14.08.2003 09:31
-----------------------------------------------*/
LateInitThread::LateInitThread()
{
}

/*-----------------------------------------------
    14.08.2003 08:42
-----------------------------------------------*/
LateInitThread::~LateInitThread()
{
}

/*-----------------------------------------------
    28.10.2003 09:30
-----------------------------------------------*/
void SAL_CALL LateInitThread::run()
{
    // sal_True => It indicates using of this method by this thread
    // The filter cache use this information to show an assertion
    // for "optimization failure" in case the first calli of loadAll()
    // was not this thread ...

    // Further please dont catch any exception here.
    // May be they show the problem of a corrupted filter
    // configuration, which is handled inside our event loop or desktop.main()!

    ::salhelper::SingletonRef< FilterCache > rCache;
    rCache->load(FilterCache::E_CONTAINS_ALL, sal_True);
}

    } // namespace config
} // namespace filter
<|endoftext|>


<commit_before>// Description: Test conversions from chromatic pitch to integer pitch.

#include "humlib.h"

using namespace hum;

int main(int argc, char** argv) {
   int dpc = 0;    // diatonic pitch class 0=C, 1=D, ..., 6=B
   int acc = 0;    // accidental alteration -2=double flat, 0=natural, 1=sharp
   int oct = 0;    // 4 = middle-C octave
   int maxacc = 0; // non-zero integer
   int wbh = 0;    // integer encoded pitch
   bool tochromatic;

   if (argc < 2) {
      // too few parameters
      return 1;
   } else if (argc < 4) {
      // convert wbh to chromatic pitch
      wbh    = atoi(argv[1]); // assuming octave will not be negative
      maxacc = atoi(argv[2]);
      tochromatic = true;
   } else if (argc > 4) {
      // convert chromatic pitch to wbh
      dpc    = atoi(argv[1]) % 7; // assuming non-negative value
      acc    = atoi(argv[2]);
      oct    = atoi(argv[3]);
      maxacc = atoi(argv[4]);
      tochromatic = false;
   } else {
      return 1;
   }

   if (tochromatic) {
      Convert::wbhToPitch(dpc, acc, oct, maxacc, wbh);
      cout << "dpc = " << dpc << endl;
      cout << "acc = " << acc << endl;
      cout << "oct = " << oct << endl;
      cout << "wbh = " << wbh << endl;
      cout << "name = ";
      switch (dpc) {
         case 0:  cout << "C"; break;
         case 1:  cout << "D"; break;
         case 2:  cout << "E"; break;
         case 3:  cout << "F"; break;
         case 4:  cout << "G"; break;
         case 5:  cout << "A"; break;
         case 6:  cout << "B"; break;
         default: cout << "X";
      }
      if (acc > 0) {
         for (int i=0; i<acc; i++) {
            cout << "#";
         }
      } else if (acc < 0) {
         for (int i=0; i<-acc; i++) {
            cout << "b";
         }
      }
      cout << oct;
      cout << endl;
   } else {
      wbh  = Convert::pitchToWbh(dpc, acc, oct, maxacc);
      cout << "wbh = " << wbh << endl;
   }

   return 0;
}

<commit_msg>Update test-wbh.cpp<commit_after>// Description: Test conversions from chromatic pitch to integer pitch.

#include "humlib.h"

using namespace hum;

int main(int argc, char** argv) {
   int dpc = 0;    // diatonic pitch class 0=C, 1=D, ..., 6=B
   int acc = 0;    // accidental alteration -2=double flat, 0=natural, 1=sharp
   int oct = 0;    // 4 = middle-C octave
   int maxacc = 0; // non-zero integer
   int wbh = 0;    // integer encoded pitch
   bool tochromatic;

   if (argc < 3) {
      // too few parameters
      return 1;
   } else if (argc < 4) {
      // convert wbh to chromatic pitch
      wbh    = atoi(argv[1]); // assuming octave will not be negative
      maxacc = atoi(argv[2]);
      tochromatic = true;
   } else if (argc > 4) {
      // convert chromatic pitch to wbh
      dpc    = atoi(argv[1]) % 7; // assuming non-negative value
      acc    = atoi(argv[2]);
      oct    = atoi(argv[3]);
      maxacc = atoi(argv[4]);
      tochromatic = false;
   } else {
      return 1;
   }

   if (tochromatic) {
      Convert::wbhToPitch(dpc, acc, oct, maxacc, wbh);
      cout << "dpc = " << dpc << endl;
      cout << "acc = " << acc << endl;
      cout << "oct = " << oct << endl;
      cout << "wbh = " << wbh << endl;
      cout << "name = ";
      switch (dpc) {
         case 0:  cout << "C"; break;
         case 1:  cout << "D"; break;
         case 2:  cout << "E"; break;
         case 3:  cout << "F"; break;
         case 4:  cout << "G"; break;
         case 5:  cout << "A"; break;
         case 6:  cout << "B"; break;
         default: cout << "X";
      }
      if (acc > 0) {
         for (int i=0; i<acc; i++) {
            cout << "#";
         }
      } else if (acc < 0) {
         for (int i=0; i<-acc; i++) {
            cout << "b";
         }
      }
      cout << oct;
      cout << endl;
   } else {
      wbh  = Convert::pitchToWbh(dpc, acc, oct, maxacc);
      cout << "wbh = " << wbh << endl;
   }

   return 0;
}

<|endoftext|>


<commit_before>#include <chrono>
#include <iostream>
#include <memory>
#include <string.h>
#include "../lcd/LcdDisplay.h"
#include "../src2/HealthStatus.h"

using namespace std;
using namespace std::chrono;

int main() {
    unique_ptr<LcdDisplay> lcd(LcdDisplay::getLcdDisplayInstance());
    lcd->init();
    StatusInformation status;
    beacon_info beacon;
    memset(&beacon, sizeof(beacon), 0);
    status.setScannerID("Room206");

    // Add some events
    sprintf(beacon.uuid, "UUID-%.10d", 66);
    beacon.minor = 66;
    beacon.major = 1;
    beacon.manufacturer = 0xdead;
    beacon.code = 0xbeef;
    beacon.count = 3;
    beacon.rssi = -68;
    milliseconds now = duration_cast<milliseconds >(high_resolution_clock::now().time_since_epoch());
    beacon.time = now.count();
    status.addEvent(beacon, false);

    beacon.isHeartbeat = true;
    beacon.minor = 206;
    beacon.time += 10;
    status.addEvent(beacon, true);

    beacon.isHeartbeat = false;
    beacon.minor = 56;
    beacon.time += 10;
    status.addEvent(beacon, false);

    beacon.isHeartbeat = true;
    beacon.minor = 206;
    beacon.time += 10;
    status.addEvent(beacon, true);

    HealthStatus healthStatus;
    healthStatus.calculateStatus(status);
    Properties statusProps = healthStatus.getLastStatus();
    char tmp[20];
    snprintf(tmp, 20, "%s:%.5d;%d", status.getScannerID().c_str(), status.getHeartbeatCount(), status.getHeartbeatRSSI());
    lcd->displayText(tmp, 0, 0);
    string uptime = statusProps["Uptime"];
    printf("%s; length=%d\n", uptime.c_str(), uptime.size());
    snprintf(tmp, 20, "UP D:dd H:hh M:mm");
    lcd->displayText(tmp, 0, 1);
    const char *load = statusProps["LoadAverage"].c_str();
    lcd->displayText(load, 0, 2);

    cout << "Enter any key to exit: ";
    std::string line; std::getline(std::cin, line);
    lcd->clear();
}
<commit_msg>events, msg counts<commit_after>#include <chrono>
#include <iostream>
#include <memory>
#include <string.h>
#include "../lcd/LcdDisplay.h"
#include "../src2/HealthStatus.h"

using namespace std;
using namespace std::chrono;

int main() {
    unique_ptr<LcdDisplay> lcd(LcdDisplay::getLcdDisplayInstance());
    lcd->init();
    StatusInformation status;
    beacon_info beacon;
    memset(&beacon, sizeof(beacon), 0);
    status.setScannerID("Room206");

    // Add some events
    sprintf(beacon.uuid, "UUID-%.10d", 66);
    beacon.minor = 66;
    beacon.major = 1;
    beacon.manufacturer = 0xdead;
    beacon.code = 0xbeef;
    beacon.count = 3;
    beacon.rssi = -68;
    milliseconds now = duration_cast<milliseconds >(high_resolution_clock::now().time_since_epoch());
    beacon.time = now.count();
    status.addEvent(beacon, false);

    beacon.isHeartbeat = true;
    beacon.minor = 206;
    beacon.time += 10;
    status.addEvent(beacon, true);

    beacon.isHeartbeat = false;
    beacon.minor = 56;
    beacon.time += 10;
    status.addEvent(beacon, false);

    beacon.isHeartbeat = true;
    beacon.minor = 206;
    beacon.time += 10;
    status.addEvent(beacon, true);

    HealthStatus healthStatus;
    healthStatus.calculateStatus(status);
    Properties statusProps = healthStatus.getLastStatus();
    char tmp[20];
    snprintf(tmp, 20, "%s:%.5d;%d", status.getScannerID().c_str(), status.getHeartbeatCount(), status.getHeartbeatRSSI());
    lcd->displayText(tmp, 0, 0);
    string uptime = statusProps["Uptime"];
    printf("%s; length=%d\n", uptime.c_str(), uptime.size());
    snprintf(tmp, 20, "UP D:dd H:hh M:mm");
    lcd->displayText(tmp, 0, 1);
    const char *load = statusProps["LoadAverage"].c_str();
    lcd->displayText(load, 0, 2);
    snprintf(tmp, 20, "Events: %d; Msgs: %d", status.getRawEventCount(), status.getPublishEventCount());
    lcd->displayText(load, 0, 3);

    cout << "Enter any key to exit: ";
    std::string line; std::getline(std::cin, line);
    lcd->clear();
}
<|endoftext|>


<commit_before>#ifndef DAVIX_PTREE_HPP
#define DAVIX_PTREE_HPP

#include <deque>
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

namespace Davix {

template <class KeyType, class DataType, class PredEqualKey = std::equal_to<KeyType>, class PredEqualData = std::equal_to<DataType> >
class BasicPtree{
public:
    typedef BasicPtree<KeyType, DataType, PredEqualKey, PredEqualData> tree_type;
    typedef tree_type* ptr_type;
    typedef std::deque<tree_type> ChildrenList;
    typedef typename ChildrenList::const_iterator const_iterator;
    typedef typename ChildrenList::iterator iterator;

    BasicPtree() :
        _key(KeyType()),
        _data(DataType()),
        _children(ChildrenList()),
        _meta_data(NULL),
        _data_cmp(PredEqualData()),
        _key_cmp(PredEqualKey())
         {}
    BasicPtree(const KeyType & key, const DataType & data, const ChildrenList & l = ChildrenList(), void* meta = NULL) :
        _key(key), _data(data), _children(l), _meta_data(meta){}
    BasicPtree(const BasicPtree & orig) : _key(orig._key), _data(orig._data),  _children(orig._children), _meta_data(orig._meta_data) {}
    ~BasicPtree(){}

    inline const_iterator beginChildren() const{
        return _children.begin();
    }

    inline iterator beginChildren(){
        return _children.begin();
    }

    inline const_iterator endChildren() const{
        return _children.end();
    }

    inline iterator endChildren(){
        return _children.end();
    }

    inline void clearChildren(){
        _children.clear();
    }

    inline const KeyType  & getKey() const {
        return _key;
    }

    inline const DataType  & getData() const {
        return _data;
    }

    inline void* getMeta(){
        return _meta_data;
    }

    inline void setMeta(void* meta){
        _meta_data = meta;
    }

    inline BasicPtree & addChild(const BasicPtree & child){
        _children.push_back(child);
        return _children.back();
    }

    inline bool compareData(const BasicPtree & other) const{
        return ( _data_cmp(_data, other._data));
    }

    inline bool compareKey(const BasicPtree & other) const{
        return ( _key_cmp(_key, other._key));
    }

    inline int compareNode(const BasicPtree & other) const{
        return (compareKey(other) && compareData(other));
    }

    inline bool matchTree(const BasicPtree & rtree) const{
        if(compareNode(rtree) == false){
            return false;
        }

        for(const_iterator it_rtree = rtree.beginChildren(); it_rtree != rtree.endChildren(); ++it_rtree){
            bool found=false;
            for(const_iterator it = beginChildren(); it != endChildren(); ++it){
                if(it->matchTree(*it_rtree) ){
                    found = true;
                    break;
                }
            }
            if(!found){ // failed to find rtree node in main tree
                return false;
            }
        }
        std::cout << " end node " << rtree._data << std::endl;
        return true;
    }

    ///
    /// Match an order chain of node as a subtree
    /// return the chain matched
    inline std::vector<ptr_type> findChain(const std::vector<tree_type> & chain){
        std::vector<ptr_type> res;
        res.reserve(chain.size());
        _findChainRec(chain.begin(), chain.end(), res);
        return res;
    }

protected:
    // members
    KeyType _key;
    DataType _data;
    ChildrenList _children;
    void* _meta_data;
    // cmp
    PredEqualData _data_cmp;
    PredEqualKey _key_cmp;

    bool _findChainRec(typename std::vector<tree_type>::const_iterator begin,
                              typename std::vector<tree_type>::const_iterator end,
                              std::vector<tree_type::ptr_type> & res){
        if(begin == end)
            return true;

        if( compareNode(*begin)){
            res.push_back(this);
            begin++;
            for(iterator it = beginChildren(); it != endChildren(); ++it){
                if( it->_findChainRec(begin, end, res) == true){
                    return true;
                }
            }
        }
        return false;
    }
};

namespace Xml{

enum NodeType{
    ElementStart=0x01,
    CData=0x02,
    Attribute=0x03,
    Comment=0x04
};

typedef BasicPtree<NodeType, std::string> XmlPTree;

} // namespace Xml

} // namespace Davix

#endif // DAVIX_PTREE_HPP
<commit_msg>- replace deque by vector, vector accept nested instanciation of incompelte type, needed for clang<commit_after>#ifndef DAVIX_PTREE_HPP
#define DAVIX_PTREE_HPP

#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

namespace Davix {

template <class KeyType, class DataType, class PredEqualKey = std::equal_to<KeyType>, class PredEqualData = std::equal_to<DataType> >
class BasicPtree{
public:
    typedef BasicPtree<KeyType, DataType, PredEqualKey, PredEqualData> tree_type;
    typedef tree_type* ptr_type;
    typedef std::vector<tree_type> ChildrenList;
    typedef typename ChildrenList::const_iterator const_iterator;
    typedef typename ChildrenList::iterator iterator;

    BasicPtree() :
        _key(KeyType()),
        _data(DataType()),
        _children(ChildrenList()),
        _meta_data(NULL),
        _data_cmp(PredEqualData()),
        _key_cmp(PredEqualKey())
         {}
    BasicPtree(const KeyType & key, const DataType & data, const ChildrenList & l = ChildrenList(), void* meta = NULL) :
        _key(key), _data(data), _children(l), _meta_data(meta){}
    BasicPtree(const BasicPtree & orig) : _key(orig._key), _data(orig._data),  _children(orig._children), _meta_data(orig._meta_data) {}
    ~BasicPtree(){}

    inline const_iterator beginChildren() const{
        return _children.begin();
    }

    inline iterator beginChildren(){
        return _children.begin();
    }

    inline const_iterator endChildren() const{
        return _children.end();
    }

    inline iterator endChildren(){
        return _children.end();
    }

    inline void clearChildren(){
        _children.clear();
    }

    inline const KeyType  & getKey() const {
        return _key;
    }

    inline const DataType  & getData() const {
        return _data;
    }

    inline void* getMeta(){
        return _meta_data;
    }

    inline void setMeta(void* meta){
        _meta_data = meta;
    }

    inline BasicPtree & addChild(const BasicPtree & child){
        _children.push_back(child);
        return _children.back();
    }

    inline bool compareData(const BasicPtree & other) const{
        return ( _data_cmp(_data, other._data));
    }

    inline bool compareKey(const BasicPtree & other) const{
        return ( _key_cmp(_key, other._key));
    }

    inline int compareNode(const BasicPtree & other) const{
        return (compareKey(other) && compareData(other));
    }

    inline bool matchTree(const BasicPtree & rtree) const{
        if(compareNode(rtree) == false){
            return false;
        }

        for(const_iterator it_rtree = rtree.beginChildren(); it_rtree != rtree.endChildren(); ++it_rtree){
            bool found=false;
            for(const_iterator it = beginChildren(); it != endChildren(); ++it){
                if(it->matchTree(*it_rtree) ){
                    found = true;
                    break;
                }
            }
            if(!found){ // failed to find rtree node in main tree
                return false;
            }
        }
        std::cout << " end node " << rtree._data << std::endl;
        return true;
    }

    ///
    /// Match an order chain of node as a subtree
    /// return the chain matched
    inline std::vector<ptr_type> findChain(const std::vector<tree_type> & chain){
        std::vector<ptr_type> res;
        res.reserve(chain.size());
        _findChainRec(chain.begin(), chain.end(), res);
        return res;
    }

protected:
    // members
    KeyType _key;
    DataType _data;
    ChildrenList _children;
    void* _meta_data;
    // cmp
    PredEqualData _data_cmp;
    PredEqualKey _key_cmp;

    bool _findChainRec(typename std::vector<tree_type>::const_iterator begin,
                              typename std::vector<tree_type>::const_iterator end,
                              std::vector<tree_type::ptr_type> & res){
        if(begin == end)
            return true;

        if( compareNode(*begin)){
            res.push_back(this);
            begin++;
            for(iterator it = beginChildren(); it != endChildren(); ++it){
                if( it->_findChainRec(begin, end, res) == true){
                    return true;
                }
            }
        }
        return false;
    }
};

namespace Xml{

enum NodeType{
    ElementStart=0x01,
    CData=0x02,
    Attribute=0x03,
    Comment=0x04
};

typedef BasicPtree<NodeType, std::string> XmlPTree;

} // namespace Xml

} // namespace Davix

#endif // DAVIX_PTREE_HPP
<|endoftext|>


<commit_before>//  Copyright 2010 Gregory Szorc
//
//  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 "zippylog/stream.hpp"

#include <zippylog/platform.hpp>

using ::google::protobuf::io::CodedInputStream;
using ::google::protobuf::io::CodedOutputStream;
using ::google::protobuf::io::FileInputStream;
using ::google::protobuf::io::FileOutputStream;
using ::std::string;

namespace zippylog {

InputStream::InputStream() :
    cis(NULL),
    version(0),
    have_next_size(false),
    next_envelope_size(0)
{ }

InputStream::~InputStream() {
    if (this->cis) delete this->cis;
}

uint32 InputStream::NextEnvelopeSize()
{
    if (this->have_next_size) {
        return this->next_envelope_size;
    }

    if (!this->cis->ReadVarint32(&this->next_envelope_size)) {
        return 0;
    }

    this->have_next_size = true;

    return this->next_envelope_size;
}

bool InputStream::ReadVersion()
{
// we read the first byte of the stream
    char v;
    if (!this->cis->ReadRaw(&v, sizeof(v))) {
        return false;
    }

    this->version = v;

    if (v != 0x01) {
        return false;
    }

    return true;
}

bool InputStream::ReadEnvelope(::zippylog::Envelope &e, uint32 &bytes_read)
{
    uint32 size = this->NextEnvelopeSize();
    if (!size) {
        bytes_read = 0;
        return false;
    }

    CodedInputStream::Limit limit = this->cis->PushLimit(size);
    if (!e.envelope.ParseFromCodedStream(this->cis) || !this->cis->ConsumedEntireMessage()) {
        this->have_next_size = false;
        bytes_read = 0;
        return false;
    }

    this->have_next_size = false;

    this->cis->PopLimit(limit);

    if (size <= 0x7f) {          // 127
        bytes_read = size + 1;
    }
    else if (size <= 0x3fff) {   // 16,383
        bytes_read = size + 2;
    }
    else if (size <= 0x1fffff) { // 2,097,151
        bytes_read = size + 3;
    }
    else {                       // 2,684,35,455 (well, actually a little less due to 32 bit limit
        bytes_read = size + 4;
    }

    return true;
}

FileInputStream::FileInputStream(const string &path, int64 offset) :
    fis(NULL)
{
    if (!this->file.Open(path, platform::File::READ | platform::File::BINARY)) {
        throw Exception("could not open file input stream");
    }

    this->fis = new ::google::protobuf::io::FileInputStream(this->file.FileDescriptor());
    this->cis = new CodedInputStream(this->fis);

    if (!this->ReadVersion()) {
        throw Exception("could not read stream version or stream version not supported");
    }

    if (offset > 0) {
        if (!this->SetAbsoluteOffset(offset)) {
            throw Exception("could not set stream offset");
        }
    }
}

FileInputStream::~FileInputStream()
{
    if (this->fis) delete this->fis;

    this->file.Close();
}

bool FileInputStream::SetAbsoluteOffset(int64 offset)
{
    if (!this->file.Seek(offset)) {
        throw Exception("could not seek to requested stream offset");
    }

    // if we change the underlying file descriptor, we need to rebuild the
    // protobuf pieces from the new offset in the descriptor b/c the
    // objects can buffer read data
    if (this->cis) {
        delete this->cis;
        this->cis = NULL;
    }

    if (this->fis) {
        delete this->fis;
        this->fis = NULL;
    }

    this->fis = new ::google::protobuf::io::FileInputStream(this->file.FileDescriptor());
    this->cis = new CodedInputStream(this->fis);

    return true;
}

OutputStream::OutputStream() : cos(NULL)
{ }

OutputStream::~OutputStream()
{
    if (this->cos) delete this->cos;
}

bool OutputStream::WriteEnvelope(::zippylog::Envelope &e)
{
    if (!this->cos) return false;

    int size = e.envelope.ByteSize();

    this->cos->WriteVarint32(size);
    return e.envelope.SerializeToCodedStream(this->cos);
}

bool OutputStream::WriteEnvelope(const void *data, int length)
{
    if (!this->cos) return false;

    this->cos->WriteVarint32(length);
    this->cos->WriteRaw(data, length);

    return true;
}

bool OutputStream::WriteStreamHeader()
{
    if (!this->cos) return false;

    char version = 0x01;
    this->cos->WriteRaw(&version, sizeof(version));
    return this->Flush();
}

bool OutputStream::WriteData(const void *data, int length)
{
    if (!this->cos) return false;

    this->cos->WriteRaw(data, length);

    return true;
}

FileOutputStream::FileOutputStream(const string &path, bool write_lock) : OutputStream(), os(NULL)
{
    int flags = platform::File::CREATE | platform::File::APPEND | platform::File::WRITE | platform::File::BINARY;

    if (write_lock) flags |= platform::File::WRITE_LOCK;

    if (!this->file.Open(path, flags)) {
        throw Exception("could not open file");
    }

    platform::FileStat stat;
    if (!platform::stat(path, stat)) {
        throw Exception("could not stat file opened for writing. weird");
    }

    if (this->cos) delete this->cos;

    this->os = new ::google::protobuf::io::FileOutputStream(this->file.FileDescriptor());
    this->cos = new CodedOutputStream(this->os);

    // this is a new file, so we need to write out the version
    if (!stat.size) {
        if (!this->WriteStreamHeader()) {
            throw Exception("could not write stream header to new stream");
        }
    }
}

FileOutputStream::~FileOutputStream()
{
    // we must delete the coded output stream first, so it returns unused
    // buffer to output stream
    if (this->cos) {
        delete this->cos;

        // we set to NULL to prevent double free in parent dtor
        this->cos = NULL;
    }

    if (this->os) {
        // we close the output stream manually, which incurs a flush
        this->os->Close();

        delete this->os;
    }
}

bool FileOutputStream::Flush()
{
    // the coded output stream takes ownership of the buffer from the file
    // output stream, which screws with the later's Flush() API (it will
    // flush the whole buffer, even though only X bytes really contain data).
    // the solution is to nuke the coded output stream, which relinquishes
    // unused bytes back to file output stream. then, we flush the output
    // stream and create a new coded output stream
    // see http://code.google.com/p/protobuf/issues/detail?id=216

    if (this->cos) {
        delete this->cos;
    }

    bool result = this->os->Flush();
    this->cos = new CodedOutputStream(this->os);

    return this->file.Flush() && result;
}

} // namespace
<commit_msg>call parent ctor; delete coded stream in dtor<commit_after>//  Copyright 2010 Gregory Szorc
//
//  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 "zippylog/stream.hpp"

#include <zippylog/platform.hpp>

using ::google::protobuf::io::CodedInputStream;
using ::google::protobuf::io::CodedOutputStream;
using ::google::protobuf::io::FileInputStream;
using ::google::protobuf::io::FileOutputStream;
using ::std::string;

namespace zippylog {

InputStream::InputStream() :
    cis(NULL),
    version(0),
    have_next_size(false),
    next_envelope_size(0)
{ }

InputStream::~InputStream() {
    if (this->cis) delete this->cis;
}

uint32 InputStream::NextEnvelopeSize()
{
    if (this->have_next_size) {
        return this->next_envelope_size;
    }

    if (!this->cis->ReadVarint32(&this->next_envelope_size)) {
        return 0;
    }

    this->have_next_size = true;

    return this->next_envelope_size;
}

bool InputStream::ReadVersion()
{
// we read the first byte of the stream
    char v;
    if (!this->cis->ReadRaw(&v, sizeof(v))) {
        return false;
    }

    this->version = v;

    if (v != 0x01) {
        return false;
    }

    return true;
}

bool InputStream::ReadEnvelope(::zippylog::Envelope &e, uint32 &bytes_read)
{
    uint32 size = this->NextEnvelopeSize();
    if (!size) {
        bytes_read = 0;
        return false;
    }

    CodedInputStream::Limit limit = this->cis->PushLimit(size);
    if (!e.envelope.ParseFromCodedStream(this->cis) || !this->cis->ConsumedEntireMessage()) {
        this->have_next_size = false;
        bytes_read = 0;
        return false;
    }

    this->have_next_size = false;

    this->cis->PopLimit(limit);

    if (size <= 0x7f) {          // 127
        bytes_read = size + 1;
    }
    else if (size <= 0x3fff) {   // 16,383
        bytes_read = size + 2;
    }
    else if (size <= 0x1fffff) { // 2,097,151
        bytes_read = size + 3;
    }
    else {                       // 2,684,35,455 (well, actually a little less due to 32 bit limit
        bytes_read = size + 4;
    }

    return true;
}

FileInputStream::FileInputStream(const string &path, int64 offset) :
    InputStream(), fis(NULL)
{
    if (!this->file.Open(path, platform::File::READ | platform::File::BINARY)) {
        throw Exception("could not open file input stream");
    }

    this->fis = new ::google::protobuf::io::FileInputStream(this->file.FileDescriptor());
    this->cis = new CodedInputStream(this->fis);

    if (!this->ReadVersion()) {
        throw Exception("could not read stream version or stream version not supported");
    }

    if (offset > 0) {
        if (!this->SetAbsoluteOffset(offset)) {
            throw Exception("could not set stream offset");
        }
    }
}

FileInputStream::~FileInputStream()
{
    // need to destroy coded stream to unassociate from file descriptor
    if (this->cis) {
        delete this->cis;
        this->cis = NULL;
    }

    if (this->fis) delete this->fis;

    this->file.Close();
}

bool FileInputStream::SetAbsoluteOffset(int64 offset)
{
    if (!this->file.Seek(offset)) {
        throw Exception("could not seek to requested stream offset");
    }

    // if we change the underlying file descriptor, we need to rebuild the
    // protobuf pieces from the new offset in the descriptor b/c the
    // objects can buffer read data
    if (this->cis) {
        delete this->cis;
        this->cis = NULL;
    }

    if (this->fis) {
        delete this->fis;
        this->fis = NULL;
    }

    this->fis = new ::google::protobuf::io::FileInputStream(this->file.FileDescriptor());
    this->cis = new CodedInputStream(this->fis);

    return true;
}

OutputStream::OutputStream() : cos(NULL)
{ }

OutputStream::~OutputStream()
{
    if (this->cos) delete this->cos;
}

bool OutputStream::WriteEnvelope(::zippylog::Envelope &e)
{
    if (!this->cos) return false;

    int size = e.envelope.ByteSize();

    this->cos->WriteVarint32(size);
    return e.envelope.SerializeToCodedStream(this->cos);
}

bool OutputStream::WriteEnvelope(const void *data, int length)
{
    if (!this->cos) return false;

    this->cos->WriteVarint32(length);
    this->cos->WriteRaw(data, length);

    return true;
}

bool OutputStream::WriteStreamHeader()
{
    if (!this->cos) return false;

    char version = 0x01;
    this->cos->WriteRaw(&version, sizeof(version));
    return this->Flush();
}

bool OutputStream::WriteData(const void *data, int length)
{
    if (!this->cos) return false;

    this->cos->WriteRaw(data, length);

    return true;
}

FileOutputStream::FileOutputStream(const string &path, bool write_lock) : OutputStream(), os(NULL)
{
    int flags = platform::File::CREATE | platform::File::APPEND | platform::File::WRITE | platform::File::BINARY;

    if (write_lock) flags |= platform::File::WRITE_LOCK;

    if (!this->file.Open(path, flags)) {
        throw Exception("could not open file");
    }

    platform::FileStat stat;
    if (!platform::stat(path, stat)) {
        throw Exception("could not stat file opened for writing. weird");
    }

    if (this->cos) delete this->cos;

    this->os = new ::google::protobuf::io::FileOutputStream(this->file.FileDescriptor());
    this->cos = new CodedOutputStream(this->os);

    // this is a new file, so we need to write out the version
    if (!stat.size) {
        if (!this->WriteStreamHeader()) {
            throw Exception("could not write stream header to new stream");
        }
    }
}

FileOutputStream::~FileOutputStream()
{
    // we must delete the coded output stream first, so it returns unused
    // buffer to output stream
    if (this->cos) {
        delete this->cos;

        // we set to NULL to prevent double free in parent dtor
        this->cos = NULL;
    }

    if (this->os) {
        // we close the output stream manually, which incurs a flush
        this->os->Close();

        delete this->os;
    }
}

bool FileOutputStream::Flush()
{
    // the coded output stream takes ownership of the buffer from the file
    // output stream, which screws with the later's Flush() API (it will
    // flush the whole buffer, even though only X bytes really contain data).
    // the solution is to nuke the coded output stream, which relinquishes
    // unused bytes back to file output stream. then, we flush the output
    // stream and create a new coded output stream
    // see http://code.google.com/p/protobuf/issues/detail?id=216

    if (this->cos) {
        delete this->cos;
    }

    bool result = this->os->Flush();
    this->cos = new CodedOutputStream(this->os);

    return this->file.Flush() && result;
}

} // namespace
<|endoftext|>


<commit_before>#ifndef __ZOOKEEPER_GROUP_HPP__
#define __ZOOKEEPER_GROUP_HPP__

#include <map>
#include <set>
#include <string>

#include "process/future.hpp"
#include "process/timer.hpp"

#include <stout/check.hpp>
#include <stout/duration.hpp>
#include <stout/none.hpp>
#include <stout/option.hpp>
#include <stout/try.hpp>

#include "zookeeper/authentication.hpp"
#include "zookeeper/url.hpp"

// Forward declarations.
class Watcher;
class ZooKeeper;

namespace zookeeper {

// Forward declaration.
class GroupProcess;

// Represents a distributed group managed by ZooKeeper. A group is
// associated with a specific ZooKeeper path, and members are
// represented by ephemeral sequential nodes.
class Group
{
public:
  // Represents a group membership. Note that we order memberships by
  // membership id (that is, an older membership is ordered before a
  // younger membership). In addition, we do not use the "cancelled"
  // future to compare memberships so that two memberships created
  // from different Group instances will still be considered the same.
  struct Membership
  {
    bool operator == (const Membership& that) const
    {
      return sequence == that.sequence;
    }

    bool operator != (const Membership& that) const
    {
      return sequence != that.sequence;
    }

    bool operator < (const Membership& that) const
    {
      return sequence < that.sequence;
    }

    bool operator <= (const Membership& that) const
    {
      return sequence <= that.sequence;
    }

    bool operator > (const Membership& that) const
    {
      return sequence > that.sequence;
    }

    bool operator >= (const Membership& that) const
    {
      return sequence >= that.sequence;
    }

    int32_t id() const
    {
      return sequence;
    }

    Option<std::string> label() const
    {
      return label_;
    }

    // Returns a future that is only satisfied once this membership
    // has been cancelled. In which case, the value of the future is
    // true if you own this membership and cancelled it by invoking
    // Group::cancel. Otherwise, the value of the future is false (and
    // could signify cancellation due to a sesssion expiration or
    // operator error).
    process::Future<bool> cancelled() const
    {
      return cancelled_;
    }

  private:
    friend class GroupProcess; // Creates and manages memberships.

    Membership(int32_t _sequence,
               const Option<std::string>& _label,
               const process::Future<bool>& cancelled)
      : sequence(_sequence), label_(_label), cancelled_(cancelled) {}

    const int32_t sequence;
    const Option<std::string> label_;
    process::Future<bool> cancelled_;
  };

  // Constructs this group using the specified ZooKeeper servers (list
  // of host:port) with the given timeout at the specified znode.
  Group(const std::string& servers,
        const Duration& timeout,
        const std::string& znode,
        const Option<Authentication>& auth = None());
  Group(const URL& url,
        const Duration& timeout);

  ~Group();

  // Returns the result of trying to join a "group" in ZooKeeper.
  // If "label" is provided the newly created znode contains "label_"
  // as the prefix. If join is successful, an "owned" membership will
  // be returned whose retrievable data will be a copy of the
  // specified parameter. A membership is not "renewed" in the event
  // of a ZooKeeper session expiration. Instead, a client should watch
  // the group memberships and rejoin the group as appropriate.
  process::Future<Membership> join(
      const std::string& data,
      const Option<std::string>& label = None());

  // Returns the result of trying to cancel a membership. Note that
  // only memberships that are "owned" (see join) can be canceled.
  process::Future<bool> cancel(const Membership& membership);

  // Returns the result of trying to fetch the data associated with a
  // group membership.
  // A None is returned if the specified membership doesn't exist,
  // e.g., it can be removed before this call can read it content.
  process::Future<Option<std::string> > data(const Membership& membership);

  // Returns a future that gets set when the group memberships differ
  // from the "expected" memberships specified.
  process::Future<std::set<Membership> > watch(
      const std::set<Membership>& expected = std::set<Membership>());

  // Returns the current ZooKeeper session associated with this group,
  // or none if no session currently exists.
  process::Future<Option<int64_t> > session();

  // Made public for testing purposes.
  GroupProcess* process;
};

class GroupProcess : public process::Process<GroupProcess>
{
public:
  GroupProcess(const std::string& servers,
               const Duration& timeout,
               const std::string& znode,
               const Option<Authentication>& auth);

  GroupProcess(const URL& url,
               const Duration& timeout);

  virtual ~GroupProcess();

  virtual void initialize();

  static const Duration RETRY_INTERVAL;

  // Helper function that returns the basename of the znode of
  // the membership.
  static std::string zkBasename(const Group::Membership& membership);

  // Group implementation.
  process::Future<Group::Membership> join(
      const std::string& data,
      const Option<std::string>& label);
  process::Future<bool> cancel(const Group::Membership& membership);
  process::Future<Option<std::string> > data(
      const Group::Membership& membership);
  process::Future<std::set<Group::Membership> > watch(
      const std::set<Group::Membership>& expected);
  process::Future<Option<int64_t> > session();

  // ZooKeeper events.
  // Note that events from previous sessions are dropped.
  void connected(int64_t sessionId, bool reconnect);
  void reconnecting(int64_t sessionId);
  void expired(int64_t sessionId);
  void updated(int64_t sessionId, const std::string& path);
  void created(int64_t sessionId, const std::string& path);
  void deleted(int64_t sessionId, const std::string& path);

private:
  Result<Group::Membership> doJoin(
      const std::string& data,
      const Option<std::string>& label);
  Result<bool> doCancel(const Group::Membership& membership);
  Result<Option<std::string> > doData(const Group::Membership& membership);

  // Returns true if authentication is successful, false if the
  // failure is retryable and Error otherwise.
  Try<bool> authenticate();

  // Creates the group (which means creating its base path) on ZK.
  // Returns true if successful, false if the failure is retryable
  // and Error otherwise.
  Try<bool> create();

  // Attempts to cache the current set of memberships.
  // Returns true if successful, false if the failure is retryable
  // and Error otherwise.
  Try<bool> cache();

  // Synchronizes pending operations with ZooKeeper and also attempts
  // to cache the current set of memberships if necessary.
  // Returns true if successful, false if the failure is retryable
  // and Error otherwise.
  Try<bool> sync();

  // Updates any pending watches.
  void update();

  // Generic retry method. This mechanism is "generic" in the sense
  // that it is not specific to any particular operation, but rather
  // attempts to perform all pending operations (including caching
  // memberships if necessary).
  void retry(const Duration& duration);

  void timedout(int64_t sessionId);

  // Aborts the group instance and fails all pending operations.
  // The group then enters an error state and all subsequent
  // operations will fail as well.
  void abort(const std::string& message);

  // Potential non-retryable error set by abort().
  Option<Error> error;

  const std::string servers;

  // The session timeout requested by the client.
  const Duration timeout;

  const std::string znode;

  Option<Authentication> auth; // ZooKeeper authentication.

  const ACL_vector acl; // Default ACL to use.

  Watcher* watcher;
  ZooKeeper* zk;

  // Group connection state.
  // Normal state transitions:
  //   DISCONNECTED -> CONNECTING -> CONNECTED -> AUTHENTICATED
  //   -> READY.
  // Reconnection does not change the current state and the state is
  // only reset to DISCONNECTED after session expiration. Therefore
  // the client's "progress" in setting up the group is preserved
  // across reconnections. This means authenticate() and create() are
  // only successfully executed once in one ZooKeeper session.
  enum State {
    DISCONNECTED,  // The initial state.
    CONNECTING,    // ZooKeeper connecting.
    CONNECTED,     // ZooKeeper connected but before group setup.
    AUTHENTICATED, // ZooKeeper connected and authenticated.
    READY,         // ZooKeeper connected, session authenticated and
                   // base path for the group created.
  } state;

  struct Join
  {
    Join(const std::string& _data, const Option<std::string>& _label)
      : data(_data), label(_label) {}
    std::string data;
    const Option<std::string> label;
    process::Promise<Group::Membership> promise;
  };

  struct Cancel
  {
    explicit Cancel(const Group::Membership& _membership)
      : membership(_membership) {}
    Group::Membership membership;
    process::Promise<bool> promise;
  };

  struct Data
  {
    explicit Data(const Group::Membership& _membership)
      : membership(_membership) {}
    Group::Membership membership;
    process::Promise<Option<std::string> > promise;
  };

  struct Watch
  {
    explicit Watch(const std::set<Group::Membership>& _expected)
      : expected(_expected) {}
    std::set<Group::Membership> expected;
    process::Promise<std::set<Group::Membership> > promise;
  };

  struct {
    std::queue<Join*> joins;
    std::queue<Cancel*> cancels;
    std::queue<Data*> datas;
    std::queue<Watch*> watches;
  } pending;

  // Indicates there is a pending delayed retry.
  bool retrying;

  // Expected ZooKeeper sequence numbers (either owned/created by this
  // group instance or not) and the promise we associate with their
  // "cancellation" (i.e., no longer part of the group).
  std::map<int32_t, process::Promise<bool>*> owned;
  std::map<int32_t, process::Promise<bool>*> unowned;

  // Cache of owned + unowned, where 'None' represents an invalid
  // cache and 'Some' represents a valid cache.
  Option<std::set<Group::Membership> > memberships;

  // The timer that determines whether we should quit waiting for the
  // connection to be restored.
  Option<process::Timer> timer;
};

} // namespace zookeeper {

#endif // __ZOOKEEPER_GROUP_HPP__
<commit_msg>Fixed missing process.hpp include in zookeeper group.<commit_after>#ifndef __ZOOKEEPER_GROUP_HPP__
#define __ZOOKEEPER_GROUP_HPP__

#include <map>
#include <set>
#include <string>

#include "process/future.hpp"
#include "process/timer.hpp"
#include "process/process.hpp"

#include <stout/check.hpp>
#include <stout/duration.hpp>
#include <stout/none.hpp>
#include <stout/option.hpp>
#include <stout/try.hpp>

#include "zookeeper/authentication.hpp"
#include "zookeeper/url.hpp"

// Forward declarations.
class Watcher;
class ZooKeeper;

namespace zookeeper {

// Forward declaration.
class GroupProcess;

// Represents a distributed group managed by ZooKeeper. A group is
// associated with a specific ZooKeeper path, and members are
// represented by ephemeral sequential nodes.
class Group
{
public:
  // Represents a group membership. Note that we order memberships by
  // membership id (that is, an older membership is ordered before a
  // younger membership). In addition, we do not use the "cancelled"
  // future to compare memberships so that two memberships created
  // from different Group instances will still be considered the same.
  struct Membership
  {
    bool operator == (const Membership& that) const
    {
      return sequence == that.sequence;
    }

    bool operator != (const Membership& that) const
    {
      return sequence != that.sequence;
    }

    bool operator < (const Membership& that) const
    {
      return sequence < that.sequence;
    }

    bool operator <= (const Membership& that) const
    {
      return sequence <= that.sequence;
    }

    bool operator > (const Membership& that) const
    {
      return sequence > that.sequence;
    }

    bool operator >= (const Membership& that) const
    {
      return sequence >= that.sequence;
    }

    int32_t id() const
    {
      return sequence;
    }

    Option<std::string> label() const
    {
      return label_;
    }

    // Returns a future that is only satisfied once this membership
    // has been cancelled. In which case, the value of the future is
    // true if you own this membership and cancelled it by invoking
    // Group::cancel. Otherwise, the value of the future is false (and
    // could signify cancellation due to a sesssion expiration or
    // operator error).
    process::Future<bool> cancelled() const
    {
      return cancelled_;
    }

  private:
    friend class GroupProcess; // Creates and manages memberships.

    Membership(int32_t _sequence,
               const Option<std::string>& _label,
               const process::Future<bool>& cancelled)
      : sequence(_sequence), label_(_label), cancelled_(cancelled) {}

    const int32_t sequence;
    const Option<std::string> label_;
    process::Future<bool> cancelled_;
  };

  // Constructs this group using the specified ZooKeeper servers (list
  // of host:port) with the given timeout at the specified znode.
  Group(const std::string& servers,
        const Duration& timeout,
        const std::string& znode,
        const Option<Authentication>& auth = None());
  Group(const URL& url,
        const Duration& timeout);

  ~Group();

  // Returns the result of trying to join a "group" in ZooKeeper.
  // If "label" is provided the newly created znode contains "label_"
  // as the prefix. If join is successful, an "owned" membership will
  // be returned whose retrievable data will be a copy of the
  // specified parameter. A membership is not "renewed" in the event
  // of a ZooKeeper session expiration. Instead, a client should watch
  // the group memberships and rejoin the group as appropriate.
  process::Future<Membership> join(
      const std::string& data,
      const Option<std::string>& label = None());

  // Returns the result of trying to cancel a membership. Note that
  // only memberships that are "owned" (see join) can be canceled.
  process::Future<bool> cancel(const Membership& membership);

  // Returns the result of trying to fetch the data associated with a
  // group membership.
  // A None is returned if the specified membership doesn't exist,
  // e.g., it can be removed before this call can read it content.
  process::Future<Option<std::string> > data(const Membership& membership);

  // Returns a future that gets set when the group memberships differ
  // from the "expected" memberships specified.
  process::Future<std::set<Membership> > watch(
      const std::set<Membership>& expected = std::set<Membership>());

  // Returns the current ZooKeeper session associated with this group,
  // or none if no session currently exists.
  process::Future<Option<int64_t> > session();

  // Made public for testing purposes.
  GroupProcess* process;
};

class GroupProcess : public process::Process<GroupProcess>
{
public:
  GroupProcess(const std::string& servers,
               const Duration& timeout,
               const std::string& znode,
               const Option<Authentication>& auth);

  GroupProcess(const URL& url,
               const Duration& timeout);

  virtual ~GroupProcess();

  virtual void initialize();

  static const Duration RETRY_INTERVAL;

  // Helper function that returns the basename of the znode of
  // the membership.
  static std::string zkBasename(const Group::Membership& membership);

  // Group implementation.
  process::Future<Group::Membership> join(
      const std::string& data,
      const Option<std::string>& label);
  process::Future<bool> cancel(const Group::Membership& membership);
  process::Future<Option<std::string> > data(
      const Group::Membership& membership);
  process::Future<std::set<Group::Membership> > watch(
      const std::set<Group::Membership>& expected);
  process::Future<Option<int64_t> > session();

  // ZooKeeper events.
  // Note that events from previous sessions are dropped.
  void connected(int64_t sessionId, bool reconnect);
  void reconnecting(int64_t sessionId);
  void expired(int64_t sessionId);
  void updated(int64_t sessionId, const std::string& path);
  void created(int64_t sessionId, const std::string& path);
  void deleted(int64_t sessionId, const std::string& path);

private:
  Result<Group::Membership> doJoin(
      const std::string& data,
      const Option<std::string>& label);
  Result<bool> doCancel(const Group::Membership& membership);
  Result<Option<std::string> > doData(const Group::Membership& membership);

  // Returns true if authentication is successful, false if the
  // failure is retryable and Error otherwise.
  Try<bool> authenticate();

  // Creates the group (which means creating its base path) on ZK.
  // Returns true if successful, false if the failure is retryable
  // and Error otherwise.
  Try<bool> create();

  // Attempts to cache the current set of memberships.
  // Returns true if successful, false if the failure is retryable
  // and Error otherwise.
  Try<bool> cache();

  // Synchronizes pending operations with ZooKeeper and also attempts
  // to cache the current set of memberships if necessary.
  // Returns true if successful, false if the failure is retryable
  // and Error otherwise.
  Try<bool> sync();

  // Updates any pending watches.
  void update();

  // Generic retry method. This mechanism is "generic" in the sense
  // that it is not specific to any particular operation, but rather
  // attempts to perform all pending operations (including caching
  // memberships if necessary).
  void retry(const Duration& duration);

  void timedout(int64_t sessionId);

  // Aborts the group instance and fails all pending operations.
  // The group then enters an error state and all subsequent
  // operations will fail as well.
  void abort(const std::string& message);

  // Potential non-retryable error set by abort().
  Option<Error> error;

  const std::string servers;

  // The session timeout requested by the client.
  const Duration timeout;

  const std::string znode;

  Option<Authentication> auth; // ZooKeeper authentication.

  const ACL_vector acl; // Default ACL to use.

  Watcher* watcher;
  ZooKeeper* zk;

  // Group connection state.
  // Normal state transitions:
  //   DISCONNECTED -> CONNECTING -> CONNECTED -> AUTHENTICATED
  //   -> READY.
  // Reconnection does not change the current state and the state is
  // only reset to DISCONNECTED after session expiration. Therefore
  // the client's "progress" in setting up the group is preserved
  // across reconnections. This means authenticate() and create() are
  // only successfully executed once in one ZooKeeper session.
  enum State {
    DISCONNECTED,  // The initial state.
    CONNECTING,    // ZooKeeper connecting.
    CONNECTED,     // ZooKeeper connected but before group setup.
    AUTHENTICATED, // ZooKeeper connected and authenticated.
    READY,         // ZooKeeper connected, session authenticated and
                   // base path for the group created.
  } state;

  struct Join
  {
    Join(const std::string& _data, const Option<std::string>& _label)
      : data(_data), label(_label) {}
    std::string data;
    const Option<std::string> label;
    process::Promise<Group::Membership> promise;
  };

  struct Cancel
  {
    explicit Cancel(const Group::Membership& _membership)
      : membership(_membership) {}
    Group::Membership membership;
    process::Promise<bool> promise;
  };

  struct Data
  {
    explicit Data(const Group::Membership& _membership)
      : membership(_membership) {}
    Group::Membership membership;
    process::Promise<Option<std::string> > promise;
  };

  struct Watch
  {
    explicit Watch(const std::set<Group::Membership>& _expected)
      : expected(_expected) {}
    std::set<Group::Membership> expected;
    process::Promise<std::set<Group::Membership> > promise;
  };

  struct {
    std::queue<Join*> joins;
    std::queue<Cancel*> cancels;
    std::queue<Data*> datas;
    std::queue<Watch*> watches;
  } pending;

  // Indicates there is a pending delayed retry.
  bool retrying;

  // Expected ZooKeeper sequence numbers (either owned/created by this
  // group instance or not) and the promise we associate with their
  // "cancellation" (i.e., no longer part of the group).
  std::map<int32_t, process::Promise<bool>*> owned;
  std::map<int32_t, process::Promise<bool>*> unowned;

  // Cache of owned + unowned, where 'None' represents an invalid
  // cache and 'Some' represents a valid cache.
  Option<std::set<Group::Membership> > memberships;

  // The timer that determines whether we should quit waiting for the
  // connection to be restored.
  Option<process::Timer> timer;
};

} // namespace zookeeper {

#endif // __ZOOKEEPER_GROUP_HPP__
<|endoftext|>


<commit_before>/** \brief Utility for displaying various bits of info about a collection of MARC records.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2018 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include "BSZUtil.h"
#include "FileUtil.h"
#include "IniFile.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    std::cerr << "Usage: " << ::progname << "\n";
    std::exit(EXIT_FAILURE);
}

const std::string CONF_FILE_PATH(
    "/usr/local/var/lib/tuelib/cronjobs/merge_differential_and_full_marc_updates.conf");

void LoadFilePCRE(std::string * const file_pcre) {
    IniFile ini_file(CONF_FILE_PATH);
    *file_pcre = ini_file.getString("Files", "deletion_list");
    *file_pcre += "|" + ini_file.getString("Files", "complete_dump");
    *file_pcre += "|" + ini_file.getString("Files", "incremental_dump");
    *file_pcre += "|" + ini_file.getString("Files", "incremental_authority_dump");
    *file_pcre += "|Complete-MARC-\\d\\d\\d\\d\\d\\d.tar.gz";
}

inline bool Contains(const std::string &haystack, const std::string &needle) {
    return haystack.find(needle) != std::string::npos;
}

// Shift a given YYMMDD to ten days after
std::string ShiftDateToTenDaysAfter(const std::string &cutoff_date) {
    struct tm cutoff_date_tm(TimeUtil::StringToStructTm(cutoff_date, "%y%m%d"));
    const time_t cutoff_date_time_t(TimeUtil::TimeGm(cutoff_date_tm));
    if (unlikely(cutoff_date_time_t == TimeUtil::BAD_TIME_T))
        LOG_ERROR("in ShiftDateToTenDaysBefore: bad time conversion! (1)");
    const time_t new_cutoff_date(TimeUtil::AddDays(cutoff_date_time_t, +10));
    if (unlikely(new_cutoff_date == TimeUtil::BAD_TIME_T))
        LOG_ERROR("in ShiftDateToTenDaysBefore: bad time conversion! (2)");
    return TimeUtil::TimeTToString(new_cutoff_date, "%y%m%d");
}

bool FileComparator(const std::string &filename1, const std::string &filename2) {
    auto date1(BSZUtil::ExtractDateFromFilenameOrDie(filename1));
    if (Contains(filename1, "sekkor"))
        date1 = ShiftDateToTenDaysAfter(date1);

    auto date2(BSZUtil::ExtractDateFromFilenameOrDie(filename2));
    if (Contains(filename2, "sekkor"))
        date2 = ShiftDateToTenDaysAfter(date2);

    if (date1 != date2)
        return date1 < date2;

    // Deletion lists come first:
    if (filename1[0] == 'L' and filename2[0] != 'L')
        return true;
    if (filename2[0] == 'L' and filename1[0] != 'L')
        return false;

    // Complete dumps come before anything else:
    if (StringUtil::StartsWith(filename1, "SA-") and not StringUtil::StartsWith(filename2, "SA-"))
        return true;
    if (StringUtil::StartsWith(filename2, "SA-") and not StringUtil::StartsWith(filename1, "SA-"))
        return false;

    // Pseudo complete dumps come before anything else:
    if (StringUtil::StartsWith(filename1, "Complete-MARC-") and not StringUtil::StartsWith(filename2, "Complete-MARC-"))
        return true;
    if (StringUtil::StartsWith(filename2, "Complete-MARC-") and not StringUtil::StartsWith(filename1, "Complete-MARC-"))
        return false;

    // Sekkor updates come before anything else:
    if (Contains(filename1, "sekkor") and not Contains(filename2, "sekkor"))
        return true;
    if (Contains(filename2, "sekkor") and not Contains(filename1, "sekkor"))
        return false;

    // Files w/o local data come before those w/ local data:
    if (Contains(filename1, "_o") and not Contains(filename2, "_o"))
        return true;
    if (Contains(filename2, "_o") and not Contains(filename1, "_o"))
        return false;

    LOG_ERROR("don't know how to compare \"" + filename1 + "\" with \"" + filename2 + "\"!");
}

inline bool IsMtexDeletionList(const std::string &filename) {
    return StringUtil::StartsWith(filename, "LOEPPN_m-");
}

bool MtexComparator(const std::string &filename1, const std::string &filename2) {
    if (IsMtexDeletionList(filename1) and IsMtexDeletionList(filename2))
        return BSZUtil::ExtractDateFromFilenameOrDie(filename1) < BSZUtil::ExtractDateFromFilenameOrDie(filename2);
    if (IsMtexDeletionList(filename1))
        return false;
    if (IsMtexDeletionList(filename2))
        return true;
    return FileComparator(filename1, filename2);
}

// Returns file_list.end() if neither a complete dump file name nor a pseudo complete dump file name were found.
std::vector<std::string>::iterator FindMostRecentCompleteOrPseudoCompleteDump(std::vector<std::string> &file_list) {
    auto file(file_list.end());
    do {
        --file;
        if (StringUtil::StartsWith(*file, "SA-") or StringUtil::StartsWith(*file, "Complete-MARC-"))
            return file;
    } while (file != file_list.begin());

    return file_list.end();
}

// If our complete dump is an SA- file, we should have a "partner" w/o local data.  In that case we should return the partner.
std::vector<std::string>::iterator EarliestReferenceDump(std::vector<std::string>::iterator complete_or_pseudo_complete_dump,
                                                         std::vector<std::string> &file_list)
{
    /* If we have found an SA- file we may have two, one w/ and one w/o local data: */
    if (StringUtil::StartsWith(*complete_or_pseudo_complete_dump, "SA-")) {
        if (complete_or_pseudo_complete_dump != file_list.begin()
            and StringUtil::StartsWith(*(complete_or_pseudo_complete_dump - 1), "SA-")
            and (BSZUtil::ExtractDateFromFilenameOrDie(*complete_or_pseudo_complete_dump)
                 == BSZUtil::ExtractDateFromFilenameOrDie(*(complete_or_pseudo_complete_dump - 1))))
            return complete_or_pseudo_complete_dump - 1;
    }

    return complete_or_pseudo_complete_dump;
}

} // unnamed namespace

int Main(int argc, char */*argv*/[]) {
    if (argc != 1)
        Usage();

    std::string file_pcre;
    LoadFilePCRE(&file_pcre);
    LOG_DEBUG("file PCRE: \"" + file_pcre + "\".");

    std::vector<std::string> file_list;
    if (FileUtil::GetFileNameList(file_pcre, &file_list) == 0)
        LOG_ERROR("no matches found for \"" + file_pcre + "\"!");

    std::sort(file_list.begin(), file_list.end(), FileComparator);
    std::stable_sort(file_list.begin(), file_list.end(), MtexComparator); // mtex deletion list must go last

    // Throw away older files before our "reference" complete dump or pseudo complete dump:
    const auto reference_dump(FindMostRecentCompleteOrPseudoCompleteDump(file_list));
    if (reference_dump == file_list.end())
        LOG_ERROR("no reference dump file found!");
    file_list.erase(file_list.begin(), EarliestReferenceDump(reference_dump, file_list));

    for (const auto &filename : file_list)
        std::cout << filename << '\n';

    return EXIT_SUCCESS;
}
<commit_msg>generate_merge_order checks for end of string (to prevent problems with e.g. tar.gz.bak!)<commit_after>/** \brief Utility for displaying various bits of info about a collection of MARC records.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2018 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include "BSZUtil.h"
#include "FileUtil.h"
#include "IniFile.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    std::cerr << "Usage: " << ::progname << "\n";
    std::exit(EXIT_FAILURE);
}

const std::string CONF_FILE_PATH(
    "/usr/local/var/lib/tuelib/cronjobs/merge_differential_and_full_marc_updates.conf");

void LoadFilePCRE(std::string * const file_pcre) {
    IniFile ini_file(CONF_FILE_PATH);
    *file_pcre = ini_file.getString("Files", "deletion_list");
    *file_pcre += "|" + ini_file.getString("Files", "complete_dump");
    *file_pcre += "|" + ini_file.getString("Files", "incremental_dump");
    *file_pcre += "|" + ini_file.getString("Files", "incremental_authority_dump");
    *file_pcre += "|Complete-MARC-\\d\\d\\d\\d\\d\\d.tar.gz$";
}

inline bool Contains(const std::string &haystack, const std::string &needle) {
    return haystack.find(needle) != std::string::npos;
}

// Shift a given YYMMDD to ten days after
std::string ShiftDateToTenDaysAfter(const std::string &cutoff_date) {
    struct tm cutoff_date_tm(TimeUtil::StringToStructTm(cutoff_date, "%y%m%d"));
    const time_t cutoff_date_time_t(TimeUtil::TimeGm(cutoff_date_tm));
    if (unlikely(cutoff_date_time_t == TimeUtil::BAD_TIME_T))
        LOG_ERROR("in ShiftDateToTenDaysBefore: bad time conversion! (1)");
    const time_t new_cutoff_date(TimeUtil::AddDays(cutoff_date_time_t, +10));
    if (unlikely(new_cutoff_date == TimeUtil::BAD_TIME_T))
        LOG_ERROR("in ShiftDateToTenDaysBefore: bad time conversion! (2)");
    return TimeUtil::TimeTToString(new_cutoff_date, "%y%m%d");
}

bool FileComparator(const std::string &filename1, const std::string &filename2) {
    auto date1(BSZUtil::ExtractDateFromFilenameOrDie(filename1));
    if (Contains(filename1, "sekkor"))
        date1 = ShiftDateToTenDaysAfter(date1);

    auto date2(BSZUtil::ExtractDateFromFilenameOrDie(filename2));
    if (Contains(filename2, "sekkor"))
        date2 = ShiftDateToTenDaysAfter(date2);

    if (date1 != date2)
        return date1 < date2;

    // Deletion lists come first:
    if (filename1[0] == 'L' and filename2[0] != 'L')
        return true;
    if (filename2[0] == 'L' and filename1[0] != 'L')
        return false;

    // Complete dumps come before anything else:
    if (StringUtil::StartsWith(filename1, "SA-") and not StringUtil::StartsWith(filename2, "SA-"))
        return true;
    if (StringUtil::StartsWith(filename2, "SA-") and not StringUtil::StartsWith(filename1, "SA-"))
        return false;

    // Pseudo complete dumps come before anything else:
    if (StringUtil::StartsWith(filename1, "Complete-MARC-") and not StringUtil::StartsWith(filename2, "Complete-MARC-"))
        return true;
    if (StringUtil::StartsWith(filename2, "Complete-MARC-") and not StringUtil::StartsWith(filename1, "Complete-MARC-"))
        return false;

    // Sekkor updates come before anything else:
    if (Contains(filename1, "sekkor") and not Contains(filename2, "sekkor"))
        return true;
    if (Contains(filename2, "sekkor") and not Contains(filename1, "sekkor"))
        return false;

    // Files w/o local data come before those w/ local data:
    if (Contains(filename1, "_o") and not Contains(filename2, "_o"))
        return true;
    if (Contains(filename2, "_o") and not Contains(filename1, "_o"))
        return false;

    LOG_ERROR("don't know how to compare \"" + filename1 + "\" with \"" + filename2 + "\"!");
}

inline bool IsMtexDeletionList(const std::string &filename) {
    return StringUtil::StartsWith(filename, "LOEPPN_m-");
}

bool MtexComparator(const std::string &filename1, const std::string &filename2) {
    if (IsMtexDeletionList(filename1) and IsMtexDeletionList(filename2))
        return BSZUtil::ExtractDateFromFilenameOrDie(filename1) < BSZUtil::ExtractDateFromFilenameOrDie(filename2);
    if (IsMtexDeletionList(filename1))
        return false;
    if (IsMtexDeletionList(filename2))
        return true;
    return FileComparator(filename1, filename2);
}

// Returns file_list.end() if neither a complete dump file name nor a pseudo complete dump file name were found.
std::vector<std::string>::iterator FindMostRecentCompleteOrPseudoCompleteDump(std::vector<std::string> &file_list) {
    auto file(file_list.end());
    do {
        --file;
        if (StringUtil::StartsWith(*file, "SA-") or StringUtil::StartsWith(*file, "Complete-MARC-"))
            return file;
    } while (file != file_list.begin());

    return file_list.end();
}

// If our complete dump is an SA- file, we should have a "partner" w/o local data.  In that case we should return the partner.
std::vector<std::string>::iterator EarliestReferenceDump(std::vector<std::string>::iterator complete_or_pseudo_complete_dump,
                                                         std::vector<std::string> &file_list)
{
    /* If we have found an SA- file we may have two, one w/ and one w/o local data: */
    if (StringUtil::StartsWith(*complete_or_pseudo_complete_dump, "SA-")) {
        if (complete_or_pseudo_complete_dump != file_list.begin()
            and StringUtil::StartsWith(*(complete_or_pseudo_complete_dump - 1), "SA-")
            and (BSZUtil::ExtractDateFromFilenameOrDie(*complete_or_pseudo_complete_dump)
                 == BSZUtil::ExtractDateFromFilenameOrDie(*(complete_or_pseudo_complete_dump - 1))))
            return complete_or_pseudo_complete_dump - 1;
    }

    return complete_or_pseudo_complete_dump;
}

} // unnamed namespace

int Main(int argc, char */*argv*/[]) {
    if (argc != 1)
        Usage();

    std::string file_pcre;
    LoadFilePCRE(&file_pcre);
    LOG_DEBUG("file PCRE: \"" + file_pcre + "\".");

    std::vector<std::string> file_list;
    if (FileUtil::GetFileNameList(file_pcre, &file_list) == 0)
        LOG_ERROR("no matches found for \"" + file_pcre + "\"!");

    std::sort(file_list.begin(), file_list.end(), FileComparator);
    std::stable_sort(file_list.begin(), file_list.end(), MtexComparator); // mtex deletion list must go last

    // Throw away older files before our "reference" complete dump or pseudo complete dump:
    const auto reference_dump(FindMostRecentCompleteOrPseudoCompleteDump(file_list));
    if (reference_dump == file_list.end())
        LOG_ERROR("no reference dump file found!");
    file_list.erase(file_list.begin(), EarliestReferenceDump(reference_dump, file_list));

    for (const auto &filename : file_list)
        std::cout << filename << '\n';

    return EXIT_SUCCESS;
}
<|endoftext|>