<commit_before>/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkBitmapDevice.h"
#include "SkCanvas.h"
#include "SkTileGrid.h"
#include "SkTileGridPicture.h"
#include "Test.h"

enum Tile {
    kTopLeft_Tile = 0x1,
    kTopRight_Tile = 0x2,
    kBottomLeft_Tile = 0x4,
    kBottomRight_Tile = 0x8,

    kAll_Tile = kTopLeft_Tile | kTopRight_Tile | kBottomLeft_Tile | kBottomRight_Tile,
};

class MockCanvas : public SkCanvas {
public:
    MockCanvas(const SkBitmap& bm) : SkCanvas(bm) {}

    virtual void drawRect(const SkRect& rect, const SkPaint&) {
        // This capture occurs before quick reject.
        fRects.push(rect);
    }

    SkTDArray<SkRect> fRects;
};

static void verifyTileHits(skiatest::Reporter* reporter, SkIRect rect,
                           uint32_t tileMask, int borderPixels = 0) {
    SkTileGridPicture::TileGridInfo info;
    info.fMargin.set(borderPixels, borderPixels);
    info.fOffset.setZero();
    info.fTileInterval.set(10 - 2 * borderPixels, 10 - 2 * borderPixels);
    SkTileGrid grid(2, 2, info, NULL);
    grid.insert(NULL, rect, false);
    REPORTER_ASSERT(reporter, grid.tileCount(0, 0) ==
                    ((tileMask & kTopLeft_Tile)? 1 : 0));
    REPORTER_ASSERT(reporter, grid.tileCount(1, 0) ==
                    ((tileMask & kTopRight_Tile)? 1 : 0));
    REPORTER_ASSERT(reporter, grid.tileCount(0, 1) ==
                    ((tileMask & kBottomLeft_Tile)? 1 : 0));
    REPORTER_ASSERT(reporter, grid.tileCount(1, 1) ==
                    ((tileMask & kBottomRight_Tile)? 1 : 0));
}

DEF_TEST(TileGrid_UnalignedQuery, reporter) {
    // Use SkTileGridPicture to generate a SkTileGrid with a helper
    SkTileGridPicture::TileGridInfo info;
    info.fMargin.setEmpty();
    info.fOffset.setZero();
    info.fTileInterval.set(10, 10);
    SkTileGridPicture picture(20, 20, info);
    SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
                                    SkIntToScalar(8), SkIntToScalar(8));
    SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(11), SkIntToScalar(11),
                                    SkIntToScalar(1), SkIntToScalar(1));
    SkCanvas* canvas = picture.beginRecording(20, 20, SkPicture::kOptimizeForClippedPlayback_RecordingFlag);
    SkPaint paint;
    canvas->drawRect(rect1, paint);
    canvas->drawRect(rect2, paint);
    picture.endRecording();

    SkBitmap store;
    store.allocN32Pixels(1, 1);

    // Test parts of top-left tile
    {
        MockCanvas mockCanvas(store);
        picture.draw(&mockCanvas);
        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
        REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
    }
    {
        MockCanvas mockCanvas(store);
        mockCanvas.translate(-7.99f, -7.99f);
        picture.draw(&mockCanvas);
        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
        REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
    }
    // Corner overlap
    {
        MockCanvas mockCanvas(store);
        mockCanvas.translate(-9.5f, -9.5f);
        picture.draw(&mockCanvas);
        REPORTER_ASSERT(reporter, 2 == mockCanvas.fRects.count());
        REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
        REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[1]);
    }
    // Intersect bottom right tile, but does not overlap rect 2
    {
        MockCanvas mockCanvas(store);
        mockCanvas.translate(-16.0f, -16.0f);
        picture.draw(&mockCanvas);
        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
        REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]);
    }
    // Out of bounds queries, snap to border tiles
    {
        MockCanvas mockCanvas(store);
        mockCanvas.translate(2.0f, 0.0f);
        picture.draw(&mockCanvas);
        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
        REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
    }
    {
        MockCanvas mockCanvas(store);
        mockCanvas.translate(0.0f, 2.0f);
        picture.draw(&mockCanvas);
        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
        REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
    }
    {
        MockCanvas mockCanvas(store);
        mockCanvas.translate(-22.0f, -16.0f);
        picture.draw(&mockCanvas);
        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
        REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]);
    }
    {
        MockCanvas mockCanvas(store);
        mockCanvas.translate(-16.0f, -22.0f);
        picture.draw(&mockCanvas);
        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
        REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]);
    }
}

DEF_TEST(TileGrid_OverlapOffsetQueryAlignment, reporter) {
    // Use SkTileGridPicture to generate a SkTileGrid with a helper
    SkTileGridPicture::TileGridInfo info;
    info.fMargin.set(1, 1);
    info.fOffset.set(-1, -1);
    info.fTileInterval.set(8, 8);
    SkTileGridPicture picture(20, 20, info);

    // rect landing entirely in top left tile
    SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
                                    SkIntToScalar(1), SkIntToScalar(1));
    // rect landing entirely in center tile
    SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(12), SkIntToScalar(12),
                                    SkIntToScalar(1), SkIntToScalar(1));
    // rect landing entirely in bottomright tile
    SkRect rect3 = SkRect::MakeXYWH(SkIntToScalar(19), SkIntToScalar(19),
                                    SkIntToScalar(1), SkIntToScalar(1));
    SkCanvas* canvas = picture.beginRecording(20, 20, SkPicture::kOptimizeForClippedPlayback_RecordingFlag);
    SkPaint paint;
    canvas->drawRect(rect1, paint);
    canvas->drawRect(rect2, paint);
    canvas->drawRect(rect3, paint);
    picture.endRecording();

    SkBitmap tileBitmap;
    tileBitmap.allocN32Pixels(10, 10);
    SkBitmap moreThanATileBitmap;
    moreThanATileBitmap.allocN32Pixels(11, 11);
    SkBitmap tinyBitmap;
    tinyBitmap.allocN32Pixels(2, 2);
    // Test parts of top-left tile
    {
        // The offset should cancel the top and left borders of the top left tile
        // So a look-up at interval 0-10 should be grid aligned,
        MockCanvas mockCanvas(tileBitmap);
        picture.draw(&mockCanvas);
        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
        REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
    }
    {
        // Encroaching border by one pixel
        MockCanvas mockCanvas(moreThanATileBitmap);
        picture.draw(&mockCanvas);
        REPORTER_ASSERT(reporter, 2 == mockCanvas.fRects.count());
        REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
        REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[1]);
    }
    {
        // Tile stride is 8 (tileWidth - 2 * border pixels
        // so translating by 8, should make query grid-aligned
        // with middle tile.
        MockCanvas mockCanvas(tileBitmap);
        mockCanvas.translate(SkIntToScalar(-8), SkIntToScalar(-8));
        picture.draw(&mockCanvas);
        REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
        REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]);
    }
    {
        MockCanvas mockCanvas(tileBitmap);
        mockCanvas.translate(-7.9f, -7.9f);
        picture.draw(&mockCanvas);
        REPORTER_ASSERT(reporter, 2 == mockCanvas.fRects.count());
        REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
        REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[1]);
    }
    {
        MockCanvas mockCanvas(tileBitmap);
        mockCanvas.translate(-8.1f, -8.1f);
        picture.draw(&mockCanvas);
        REPORTER_ASSERT(reporter, 2 == mockCanvas.fRects.count());
        REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]);
        REPORTER_ASSERT(reporter, rect3 == mockCanvas.fRects[1]);
    }
    {
        // Regression test for crbug.com/234688
        // Once the 2x2 device region is inset by margin, it yields an empty
        // adjusted region, sitting right on top of the tile boundary.
        MockCanvas mockCanvas(tinyBitmap);
        mockCanvas.translate(-8.0f, -8.0f);
        picture.draw(&mockCanvas);
        // This test passes by not asserting. We do not validate the rects recorded
        // because the result is numerically unstable (floating point equality).
        // The content of any one of the four tiles of the tilegrid would be a valid
        // result since any bbox that covers the center point of the canvas will be
        // recorded in all four tiles.
    }
}

DEF_TEST(TileGrid, reporter) {
    // Out of bounds
    verifyTileHits(reporter, SkIRect::MakeXYWH(30, 0, 1, 1),  0);
    verifyTileHits(reporter, SkIRect::MakeXYWH(0, 30, 1, 1),  0);
    verifyTileHits(reporter, SkIRect::MakeXYWH(-10, 0, 1, 1),  0);
    verifyTileHits(reporter, SkIRect::MakeXYWH(0, -10, 1, 1),  0);

    // Dilation for AA consideration
    verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 9, 9),  kTopLeft_Tile);
    verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 10, 10),  kAll_Tile);
    verifyTileHits(reporter, SkIRect::MakeXYWH(9, 9, 1, 1),  kAll_Tile);
    verifyTileHits(reporter, SkIRect::MakeXYWH(10, 10, 1, 1),  kAll_Tile);
    verifyTileHits(reporter, SkIRect::MakeXYWH(11, 11, 1, 1),  kBottomRight_Tile);

    // BorderPixels
    verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 6, 6),  kTopLeft_Tile, 1);
    verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 7, 7),  kAll_Tile, 1);
    verifyTileHits(reporter, SkIRect::MakeXYWH(9, 9, 1, 1),  kAll_Tile, 1);
    verifyTileHits(reporter, SkIRect::MakeXYWH(10, 10, 1, 1),  kBottomRight_Tile, 1);
    verifyTileHits(reporter, SkIRect::MakeXYWH(17, 17, 1, 1),  kBottomRight_Tile, 1);

    // BBoxes that overlap tiles
    verifyTileHits(reporter, SkIRect::MakeXYWH(5, 5, 10, 1),  kTopLeft_Tile | kTopRight_Tile);
    verifyTileHits(reporter, SkIRect::MakeXYWH(5, 5, 1, 10),  kTopLeft_Tile |
                   kBottomLeft_Tile);
    verifyTileHits(reporter, SkIRect::MakeXYWH(5, 5, 10, 10),  kAll_Tile);
    verifyTileHits(reporter, SkIRect::MakeXYWH(-10, -10, 40, 40),  kAll_Tile);
}
<commit_msg>Change tilegrid test to test it directly, rather than through SkPicture This is necessary because it makes assumptions that picture will draw all the rects that match the grids, which may not hold if picture decides to improve the accuracy of the results.<commit_after>/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkBitmapDevice.h"
#include "SkCanvas.h"
#include "SkTileGrid.h"
#include "SkTileGridPicture.h"
#include "Test.h"

enum Tile {
    kTopLeft_Tile = 0x1,
    kTopRight_Tile = 0x2,
    kBottomLeft_Tile = 0x4,
    kBottomRight_Tile = 0x8,

    kAll_Tile = kTopLeft_Tile | kTopRight_Tile | kBottomLeft_Tile | kBottomRight_Tile,
};

static void verifyTileHits(skiatest::Reporter* reporter, SkIRect rect,
                           uint32_t tileMask, int borderPixels = 0) {
    SkTileGridPicture::TileGridInfo info;
    info.fMargin.set(borderPixels, borderPixels);
    info.fOffset.setZero();
    info.fTileInterval.set(10 - 2 * borderPixels, 10 - 2 * borderPixels);
    SkTileGrid grid(2, 2, info, NULL);
    grid.insert(NULL, rect, false);
    REPORTER_ASSERT(reporter, grid.tileCount(0, 0) ==
                    ((tileMask & kTopLeft_Tile)? 1 : 0));
    REPORTER_ASSERT(reporter, grid.tileCount(1, 0) ==
                    ((tileMask & kTopRight_Tile)? 1 : 0));
    REPORTER_ASSERT(reporter, grid.tileCount(0, 1) ==
                    ((tileMask & kBottomLeft_Tile)? 1 : 0));
    REPORTER_ASSERT(reporter, grid.tileCount(1, 1) ==
                    ((tileMask & kBottomRight_Tile)? 1 : 0));
}

static SkIRect query(float x, float y, float w, float h) {
    // inflate for the margin++ in tilegrid
    SkRect bounds = SkRect::MakeXYWH(x, y, w, h);
    SkIRect r;
    bounds.roundOut(&r);
    r.outset(1, 1); // to counteract the inset in SkTileGrid::search
    return r;
}

DEF_TEST(TileGrid_UnalignedQuery, reporter) {
    SkTileGridPicture::TileGridInfo info;
    info.fMargin.setEmpty();
    info.fOffset.setZero();
    info.fTileInterval.set(10, 10);
    SkIRect rect1 = SkIRect::MakeXYWH(0, 0, 8, 8);
    SkIRect rect2 = SkIRect::MakeXYWH(11, 11, 1, 1);
    SkTileGrid grid(2, 2, info, SkTileGridNextDatum<SkPictureStateTree::Draw>);
    grid.insert(&rect1, rect1, true);
    grid.insert(&rect2, rect2, true);

    // Test parts of top-left tile
    {
        SkTDArray<void*> rects;
        grid.search(query(0.0f, 0.0f, 1.0f, 1.0f), &rects);
        REPORTER_ASSERT(reporter, 1 == rects.count());
        REPORTER_ASSERT(reporter, &rect1 == rects[0]);
    }
    {
        SkTDArray<void*> rects;
        grid.search(query(7.99f, 7.99f, 1.0f, 1.0f), &rects);
        REPORTER_ASSERT(reporter, 1 == rects.count());
        REPORTER_ASSERT(reporter, &rect1 == rects[0]);
    }
    // Corner overlap
    {
        SkTDArray<void*> rects;
        grid.search(query(9.5f, 9.5f, 1.0f, 1.0f), &rects);
        REPORTER_ASSERT(reporter, 2 == rects.count());
        REPORTER_ASSERT(reporter, &rect1 == rects[0]);
        REPORTER_ASSERT(reporter, &rect2 == rects[1]);
    }
    // Intersect bottom right tile, but does not overlap rect 2
    {
        SkTDArray<void*> rects;
        grid.search(query(16.0f, 16.0f, 1.0f, 1.0f), &rects);
        REPORTER_ASSERT(reporter, 1 == rects.count());
        REPORTER_ASSERT(reporter, &rect2 == rects[0]);
    }
    // Out of bounds queries, snap to border tiles
    {
        SkTDArray<void*> rects;
        grid.search(query(-2.0f, 0.0f, 1.0f, 1.0f), &rects);
        REPORTER_ASSERT(reporter, 1 == rects.count());
        REPORTER_ASSERT(reporter, &rect1 == rects[0]);
    }
    {
        SkTDArray<void*> rects;
        grid.search(query(0.0f, -2.0f, 1.0f, 1.0f), &rects);
        REPORTER_ASSERT(reporter, 1 == rects.count());
        REPORTER_ASSERT(reporter, &rect1 == rects[0]);
    }
    {
        SkTDArray<void*> rects;
        grid.search(query(22.0f, 16.0f, 1.0f, 1.0f), &rects);
        REPORTER_ASSERT(reporter, 1 == rects.count());
        REPORTER_ASSERT(reporter, &rect2 == rects[0]);
    }
    {
        SkTDArray<void*> rects;
        grid.search(query(16.0f, 22.0f, 1.0f, 1.0f), &rects);
        REPORTER_ASSERT(reporter, 1 == rects.count());
        REPORTER_ASSERT(reporter, &rect2 == rects[0]);
    }
}

DEF_TEST(TileGrid_OverlapOffsetQueryAlignment, reporter) {
    // Use SkTileGridPicture to generate a SkTileGrid with a helper
    SkTileGridPicture::TileGridInfo info;
    info.fMargin.set(1, 1);
    info.fOffset.set(-1, -1);
    info.fTileInterval.set(8, 8);

    // rect landing entirely in top left tile
    SkIRect rect1 = SkIRect::MakeXYWH(0, 0, 1, 1);
    // rect landing entirely in center tile
    SkIRect rect2 = SkIRect::MakeXYWH(12, 12, 1, 1);
    // rect landing entirely in bottomright tile
    SkIRect rect3 = SkIRect::MakeXYWH(19, 19, 1, 1);
    SkTileGrid grid(3, 3, info, SkTileGridNextDatum<SkPictureStateTree::Draw>);
    grid.insert(&rect1, rect1, true);
    grid.insert(&rect2, rect2, true);
    grid.insert(&rect3, rect3, true);

    // Test parts of top-left tile
    {
        // The offset should cancel the top and left borders of the top left tile
        // So a look-up at interval 0-10 should be grid aligned,
        SkTDArray<void*> rects;
        grid.search(query(0.0f, 0.0f, 10.0f, 10.0f), &rects);
        REPORTER_ASSERT(reporter, 1 == rects.count());
        REPORTER_ASSERT(reporter, &rect1 == rects[0]);
    }
    {
        // Encroaching border by one pixel
        SkTDArray<void*> rects;
        grid.search(query(0.0f, 0.0f, 11.0f, 11.0f), &rects);
        REPORTER_ASSERT(reporter, 2 == rects.count());
        REPORTER_ASSERT(reporter, &rect1 == rects[0]);
        REPORTER_ASSERT(reporter, &rect2 == rects[1]);
    }
    {
        // Tile stride is 8 (tileWidth - 2 * border pixels
        // so translating by 8, should make query grid-aligned
        // with middle tile.
        SkTDArray<void*> rects;
        grid.search(query(8.0f, 8.0f, 10.0f, 10.0f), &rects);
        REPORTER_ASSERT(reporter, 1 == rects.count());
        REPORTER_ASSERT(reporter, &rect2 == rects[0]);
    }
    {
        SkTDArray<void*> rects;
        grid.search(query(7.9f, 7.9f, 10.0f, 10.0f), &rects);
        REPORTER_ASSERT(reporter, 2 == rects.count());
        REPORTER_ASSERT(reporter, &rect1 == rects[0]);
        REPORTER_ASSERT(reporter, &rect2 == rects[1]);
    }
    {
        SkTDArray<void*> rects;
        grid.search(query(8.1f, 8.1f, 10.0f, 10.0f), &rects);
        REPORTER_ASSERT(reporter, 2 == rects.count());
        REPORTER_ASSERT(reporter, &rect2 == rects[0]);
        REPORTER_ASSERT(reporter, &rect3 == rects[1]);
    }
    {
        // Regression test for crbug.com/234688
        // Once the 2x2 device region is inset by margin, it yields an empty
        // adjusted region, sitting right on top of the tile boundary.
        SkTDArray<void*> rects;
        grid.search(query(8.0f, 8.0f, 2.0f, 2.0f), &rects);
        // This test passes by not asserting. We do not validate the rects recorded
        // because the result is numerically unstable (floating point equality).
        // The content of any one of the four tiles of the tilegrid would be a valid
        // result since any bbox that covers the center point of the canvas will be
        // recorded in all four tiles.
    }
}

DEF_TEST(TileGrid, reporter) {
    // Out of bounds
    verifyTileHits(reporter, SkIRect::MakeXYWH(30, 0, 1, 1),  0);
    verifyTileHits(reporter, SkIRect::MakeXYWH(0, 30, 1, 1),  0);
    verifyTileHits(reporter, SkIRect::MakeXYWH(-10, 0, 1, 1),  0);
    verifyTileHits(reporter, SkIRect::MakeXYWH(0, -10, 1, 1),  0);

    // Dilation for AA consideration
    verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 9, 9),  kTopLeft_Tile);
    verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 10, 10),  kAll_Tile);
    verifyTileHits(reporter, SkIRect::MakeXYWH(9, 9, 1, 1),  kAll_Tile);
    verifyTileHits(reporter, SkIRect::MakeXYWH(10, 10, 1, 1),  kAll_Tile);
    verifyTileHits(reporter, SkIRect::MakeXYWH(11, 11, 1, 1),  kBottomRight_Tile);

    // BorderPixels
    verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 6, 6),  kTopLeft_Tile, 1);
    verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 7, 7),  kAll_Tile, 1);
    verifyTileHits(reporter, SkIRect::MakeXYWH(9, 9, 1, 1),  kAll_Tile, 1);
    verifyTileHits(reporter, SkIRect::MakeXYWH(10, 10, 1, 1),  kBottomRight_Tile, 1);
    verifyTileHits(reporter, SkIRect::MakeXYWH(17, 17, 1, 1),  kBottomRight_Tile, 1);

    // BBoxes that overlap tiles
    verifyTileHits(reporter, SkIRect::MakeXYWH(5, 5, 10, 1),  kTopLeft_Tile | kTopRight_Tile);
    verifyTileHits(reporter, SkIRect::MakeXYWH(5, 5, 1, 10),  kTopLeft_Tile |
                   kBottomLeft_Tile);
    verifyTileHits(reporter, SkIRect::MakeXYWH(5, 5, 10, 10),  kAll_Tile);
    verifyTileHits(reporter, SkIRect::MakeXYWH(-10, -10, 40, 40),  kAll_Tile);
}
<|endoftext|>


<commit_before>#include <mv_action.h>
#include <table.h>

extern Table** mv_tables;

Action::Action()
{
        this->__version = 0;
        this->__combinedHash = 0;
        this->__readonly = false;
        this->__state = STICKY;
        for (uint32_t i = 0; i < NUM_CC_THREADS; ++i) {
                this->__write_starts.push_back(-1);
                this->__read_starts.push_back(-1);
        }
}

CompositeKey Action::GenerateKey(bool is_rmw, uint32_t tableId, uint64_t key)
{
        CompositeKey toAdd(is_rmw, tableId, key);
        uint32_t threadId =
                CompositeKey::HashKey(&toAdd) % NUM_CC_THREADS;
        toAdd.threadId = threadId;
        this->__combinedHash |= ((uint64_t)1) << threadId;
        return toAdd;
}

void* Action::Read(uint32_t index)
{
        //        uint64_t key = __readset[index].key;
        //        return mv_tables[0]->Get(key);
        MVRecord *record = __readset[index].value;
        if (__readonly == true &&
            GET_MV_EPOCH(__version) == GET_MV_EPOCH(record->createTimestamp)) {
                MVRecord *snapshot = record->epoch_ancestor;
                return (void*)snapshot->value;
        } else {
                return (void*)__readset[index].value->value;
        }
}

void* Action::GetWriteRef(uint32_t index)
{
        //        uint64_t key = __writeset[index].key;
        //        return mv_tables[0]->Get(key);
        MVRecord *record = __writeset[index].value;
        assert(record->value != NULL);
        return record->value;
}

void* Action::ReadWrite(uint32_t index)
{
        //        uint64_t key = __writeset[index].key;
        //        return mv_tables[0]->Get(key);
        assert(__writeset[index].is_rmw);
        MVRecord *record = __writeset[index].value;
        return (void*)record->recordLink->value;
}

void Action::AddReadKey(uint32_t tableId, uint64_t key)
{
        CompositeKey to_add;
        to_add = GenerateKey(false, tableId, key);
        __readset.push_back(to_add);
}

void Action::AddWriteKey(uint32_t tableId, uint64_t key, bool is_rmw)
{
        CompositeKey to_add;
        to_add = GenerateKey(is_rmw, tableId, key);
        __writeset.push_back(to_add);
        __readonly = false;
}

InsertAction::InsertAction() : Action()
{
}

bool InsertAction::Run()
{
        /*
        uint32_t num_writes, i, j;
        uint64_t *ref, key;
        if (recordSize == 8) {
                num_writes = __writeset.size();
                for (i = 0; i < num_writes; ++i) {
                        key = __writeset[i].key;
                        ref = (uint64_t*)GetWriteRef(i);
                        *ref = key;
                }
        } else if (recordSize == 1000) {
                num_writes = __writeset.size();
                for (i = 0; i < num_writes; ++i) {
                        ref = (uint64_t*)GetWriteRef(i);
                        for (j = 0; j < 125; ++j)
                                ref[j] = (uint64_t)rand();

                }
        } else {
                assert(false);
        }
        */
        return true;
}

mv_readonly::mv_readonly()
{
        __readonly = true;
}

bool mv_readonly::Run()
{
        uint32_t i, j, num_reads;
        assert(__readonly == true);
        num_reads = __readset.size();
        for (i = 0; i < num_reads; ++i) {
                char *read_ptr = (char*)Read(i);
                for (j = 0; j < 10; ++j) {
                        uint64_t *write_p = (uint64_t*)&__reads[j*100];
                        *write_p += *((uint64_t*)&read_ptr[j*100]);
                }
        }

        return true;
}

bool mv_mix_action::Run()
{
        uint32_t i, j, num_reads, num_writes;
        num_writes = __writeset.size();
        num_reads = __readset.size();
        for (i = 0; i < num_reads; ++i) {
                char *read_ptr = (char*)Read(i);
                for (j = 0; j < 10; ++j) {
                        uint32_t *write_p = (uint32_t*)&__reads[j*100];
                        *write_p += *((uint32_t*)&read_ptr[j*100]);
                }
        }
        for (i = 0; i < num_writes; ++i) {
                if (__writeset[i].is_rmw == true) {
                        char *read_ptr = (char*)ReadWrite(i);
                        for (j = 0; j < 10; ++j) {
                                uint32_t *write_p = (uint32_t*)&__reads[j*100];
                                *write_p += *((uint32_t*)&read_ptr[j*100]);
                        }
                } else {
                        break;
                }
        }
        for (i = 0; i < num_writes; ++i) {
                char *ptr = (char*)GetWriteRef(i);
                memcpy(ptr, __reads, 1000);
                for (j = 0; j < 10; ++j) {
                        uint32_t *write_p = (uint32_t*)&ptr[j*100];
                        *write_p += i+j;
                }
        }
        return true;
}

RMWAction::RMWAction(uint64_t seed) : Action()
{
        __total = seed;
}

void RMWAction::DoReads()
{
        uint32_t num_fields, num_reads, num_writes, i, j;
        uint64_t *field_ptr, counter;
        counter = 0;
        num_fields = recordSize/sizeof(uint64_t);
        num_reads = __readset.size();
        num_writes = __writeset.size();
        for (i = 0; i < num_reads; ++i) {
                field_ptr = (uint64_t*)Read(i);
                for (j = 0; j < num_fields; ++j)
                        counter += field_ptr[j];
        }
        for (i = 0; i < num_writes; ++i) {
                if (__writeset[i].is_rmw == true) {
                        field_ptr = (uint64_t*)ReadWrite(i);
                        for (j = 0; j < num_fields; ++j)
                                counter += field_ptr[j];
                }
        }
}

void RMWAction::AccumulateValues()
{
        /*
        uint32_t i, num_fields;
        num_fields = recordSize/sizeof(uint64_t);
        __total = 0;
        for (i = 0; i < num_fields; ++i)
                __total += __accumulated[i];
        */

}

void RMWAction::DoWrites()
{
        uint32_t i, j, num_writes, num_fields;
        char *field_ptr;
        num_fields = 10;
        num_writes = __writeset.size();
        for (i = 0; i < num_writes; ++i) {
                assert(__writeset[i].is_rmw == true);
                memcpy(GetWriteRef(i), ReadWrite(i), 1000);
                field_ptr = (char*)GetWriteRef(i);
                for (j = 0; j < num_fields; ++j) {
                        *((uint32_t*)&field_ptr[j*100]) += j+1;
                }
        }
}

bool RMWAction::Run()
{
        uint32_t i, j, num_reads, num_writes, num_fields;
        assert(recordSize == 1000);
        num_reads = __readset.size();
        num_writes = __writeset.size();
        num_fields = YCSB_RECORD_SIZE / 100;
        uint64_t counter = 0;
        for (i = 0; i < num_reads; ++i) {
                char *field_ptr = (char*)Read(i);
                if (SMALL_RECORDS) {
                        counter += *((uint64_t*)field_ptr);
                } else {
                        for (j = 0; j < num_fields; ++j)
                                counter += *((uint64_t*)&field_ptr[j*100]);
                }
        }
        for (i = 0; i < num_writes; ++i) {
                if (__writeset[i].is_rmw) {
                        char *field_ptr = (char*)ReadWrite(i);
                        if (SMALL_RECORDS) {
                                counter += *((uint64_t*)field_ptr);
                        } else {
                                for (j = 0; j < num_fields; ++j)
                                        counter += *((uint64_t*)&field_ptr[j*100]);
                        }
                }
        }

        for (i = 0; i < num_writes; ++i) {
                assert(__writeset[i].is_rmw);
                char *read_ptr = (char*)ReadWrite(i);
                char *write_ptr = (char*)GetWriteRef(i);
                if (SMALL_RECORDS) {
                        *((uint64_t*)write_ptr) =
                                counter + *((uint64_t*)read_ptr);
                } else {
                        memcpy(write_ptr, read_ptr, YCSB_RECORD_SIZE);
                        for (j = 0; j < num_fields; ++j)
                                *((uint64_t*)&write_ptr[j*100]) += j+1+counter;
                        //              *((uint64_t*)&read_ptr[j*100]);
                }
        }

        return true;
}

mv_action::mv_action(txn *t) : translator(t)
{
        this->__version = 0;
        this->__combinedHash = 0;
        this->__readonly = false;
        this->__state = STICKY;
        for (uint32_t i = 0; i < NUM_CC_THREADS; ++i) {
                this->__write_starts.push_back(-1);
                this->__read_starts.push_back(-1);
        }
        this->init = false;
}

bool mv_action::initialized()
{
        return init;
}

static struct big_key get_key(CompositeKey k)
{
        struct big_key ret;
        ret.key = k.key;
        ret.table_id = k.tableId;
        return ret;
}

/*
 * Assumes that all the entries in the transaction's read- and write-sets are
 * initialized.
 */
void mv_action::setup_reverse_index()
{
        uint32_t num_reads, num_writes, i;
        struct big_key key;
        struct key_index index;

        assert(init == false);
        num_reads = __readset.size();
        num_writes = __writeset.size();

        /* Initialize reverse index to items in the write-set. */
        for (i = 0; i < num_writes; ++i) {
                key = get_key(__writeset[i]);
                index.index = i;
                index.initialized = false;
                if (__writeset[i].is_rmw == true)
                        index.use = RMW;
                else
                        index.use = WRITE;
                assert(reverse_index.count(key) == 0);
                reverse_index[key] = index;
        }

        /* Initialize reverse index to items in the read-set. */
        for (i = 0; i < num_reads; ++i) {
                key = get_key(__readset[i]);
                index.index = i;
                index.initialized = true;
                index.use = READ;
                assert(reverse_index.count(key) == 0);
                reverse_index[key] = index;
        }

        /* Optimize read-only txns in the execution phase. */
        if (num_writes == 0)
                __readonly = true;
        else
                __readonly = false;
        init = true;
}

bool mv_action::Run()
{
        return t->Run();
}

void* mv_action::write_ref(uint64_t key, uint32_t table_id)
{
        //        struct big_key bkey;
        //        struct key_index index;
        uint32_t num_writes, i;

        assert(init == true);
        num_writes = this->__writeset.size();
        //        assert(num_writes < 10);
        for (i = 0; i < num_writes; ++i) {
                if (this->__writeset[i].key == key &&
                    this->__writeset[i].tableId == table_id) {
                        assert(this->__writeset[i].initialized == true);
                        return this->__writeset[i].value->value;
                }
        }
        assert(false);
        return NULL;
}

void* mv_action::read(uint64_t key, uint32_t table_id)
{
        //        struct big_key bkey;
        //        struct key_index index;
        MVRecord *record, *snapshot;
        uint32_t i, num_reads;
        void *ret;

        assert(init == true);
        record = NULL;
        num_reads = this->__readset.size();
        //        assert(num_reads < 10);
        for (i = 0; i < num_reads; ++i) {
                if (this->__readset[i].key == key &&
                    this->__readset[i].tableId == table_id) {
                        record = this->__readset[i].value;
                        break;
                }
        }
        assert(record != NULL);
        if (this->__readonly == true &&
            (
             GET_MV_EPOCH(this->__version) ==
             GET_MV_EPOCH(record->createTimestamp)
             )) {
                snapshot = record->epoch_ancestor;
                ret = (void*)snapshot->value;
        } else {
                ret = (void*)record->value;
        }
        return ret;

        /*
        bkey.key = key;
        bkey.table_id = table_id;
        assert(reverse_index.count(bkey) == 1);
        index = reverse_index[bkey];
        assert(index.use == READ || index.use == RMW);
        if (index.use == READ) {
                record = __readset[index.index].value;
                if (__readonly == true &&
                    (
                     GET_MV_EPOCH(__version) ==
                     GET_MV_EPOCH(record->createTimestamp)
                     )) {
                        snapshot = record->epoch_ancestor;
                        ret = (void*)snapshot->value;
                } else {
                        ret = (void*)__readset[index.index].value->value;
                }
        } else {	// index.use == RMW
                assert(__readonly == false);
                ret = __writeset[index.index].value->value;
        }
        return ret;
        */
}

CompositeKey mv_action::GenerateKey(bool is_rmw, uint32_t tableId, uint64_t key)
{
        CompositeKey toAdd(is_rmw, tableId, key);
        uint32_t threadId =
                CompositeKey::HashKey(&toAdd) % NUM_CC_THREADS;
        toAdd.threadId = threadId;
        this->__combinedHash |= ((uint64_t)1) << threadId;
        return toAdd;
}

void mv_action::add_read_key(uint32_t tableId, uint64_t key)
{
        CompositeKey to_add;
        assert(tableId == 0 || tableId == 1);
        to_add = GenerateKey(false, tableId, key);
        __readset.push_back(to_add);
}

void mv_action::add_write_key(uint32_t tableId, uint64_t key, bool is_rmw)
{
        CompositeKey to_add;
        assert(tableId == 0 || tableId == 1);
        to_add = GenerateKey(is_rmw, tableId, key);
        __writeset.push_back(to_add);
        __readonly = false;
}
<commit_msg>Fix assertion for RMW records.<commit_after>#include <mv_action.h>
#include <table.h>

extern Table** mv_tables;

Action::Action()
{
        this->__version = 0;
        this->__combinedHash = 0;
        this->__readonly = false;
        this->__state = STICKY;
        for (uint32_t i = 0; i < NUM_CC_THREADS; ++i) {
                this->__write_starts.push_back(-1);
                this->__read_starts.push_back(-1);
        }
}

CompositeKey Action::GenerateKey(bool is_rmw, uint32_t tableId, uint64_t key)
{
        CompositeKey toAdd(is_rmw, tableId, key);
        uint32_t threadId =
                CompositeKey::HashKey(&toAdd) % NUM_CC_THREADS;
        toAdd.threadId = threadId;
        this->__combinedHash |= ((uint64_t)1) << threadId;
        return toAdd;
}

void* Action::Read(uint32_t index)
{
        //        uint64_t key = __readset[index].key;
        //        return mv_tables[0]->Get(key);
        MVRecord *record = __readset[index].value;
        if (__readonly == true &&
            GET_MV_EPOCH(__version) == GET_MV_EPOCH(record->createTimestamp)) {
                MVRecord *snapshot = record->epoch_ancestor;
                return (void*)snapshot->value;
        } else {
                return (void*)__readset[index].value->value;
        }
}

void* Action::GetWriteRef(uint32_t index)
{
        //        uint64_t key = __writeset[index].key;
        //        return mv_tables[0]->Get(key);
        MVRecord *record = __writeset[index].value;
        assert(record->value != NULL);
        return record->value;
}

void* Action::ReadWrite(uint32_t index)
{
        //        uint64_t key = __writeset[index].key;
        //        return mv_tables[0]->Get(key);
        assert(__writeset[index].is_rmw);
        MVRecord *record = __writeset[index].value;
        return (void*)record->recordLink->value;
}

void Action::AddReadKey(uint32_t tableId, uint64_t key)
{
        CompositeKey to_add;
        to_add = GenerateKey(false, tableId, key);
        __readset.push_back(to_add);
}

void Action::AddWriteKey(uint32_t tableId, uint64_t key, bool is_rmw)
{
        CompositeKey to_add;
        to_add = GenerateKey(is_rmw, tableId, key);
        __writeset.push_back(to_add);
        __readonly = false;
}

InsertAction::InsertAction() : Action()
{
}

bool InsertAction::Run()
{
        /*
        uint32_t num_writes, i, j;
        uint64_t *ref, key;
        if (recordSize == 8) {
                num_writes = __writeset.size();
                for (i = 0; i < num_writes; ++i) {
                        key = __writeset[i].key;
                        ref = (uint64_t*)GetWriteRef(i);
                        *ref = key;
                }
        } else if (recordSize == 1000) {
                num_writes = __writeset.size();
                for (i = 0; i < num_writes; ++i) {
                        ref = (uint64_t*)GetWriteRef(i);
                        for (j = 0; j < 125; ++j)
                                ref[j] = (uint64_t)rand();

                }
        } else {
                assert(false);
        }
        */
        return true;
}

mv_readonly::mv_readonly()
{
        __readonly = true;
}

bool mv_readonly::Run()
{
        uint32_t i, j, num_reads;
        assert(__readonly == true);
        num_reads = __readset.size();
        for (i = 0; i < num_reads; ++i) {
                char *read_ptr = (char*)Read(i);
                for (j = 0; j < 10; ++j) {
                        uint64_t *write_p = (uint64_t*)&__reads[j*100];
                        *write_p += *((uint64_t*)&read_ptr[j*100]);
                }
        }

        return true;
}

bool mv_mix_action::Run()
{
        uint32_t i, j, num_reads, num_writes;
        num_writes = __writeset.size();
        num_reads = __readset.size();
        for (i = 0; i < num_reads; ++i) {
                char *read_ptr = (char*)Read(i);
                for (j = 0; j < 10; ++j) {
                        uint32_t *write_p = (uint32_t*)&__reads[j*100];
                        *write_p += *((uint32_t*)&read_ptr[j*100]);
                }
        }
        for (i = 0; i < num_writes; ++i) {
                if (__writeset[i].is_rmw == true) {
                        char *read_ptr = (char*)ReadWrite(i);
                        for (j = 0; j < 10; ++j) {
                                uint32_t *write_p = (uint32_t*)&__reads[j*100];
                                *write_p += *((uint32_t*)&read_ptr[j*100]);
                        }
                } else {
                        break;
                }
        }
        for (i = 0; i < num_writes; ++i) {
                char *ptr = (char*)GetWriteRef(i);
                memcpy(ptr, __reads, 1000);
                for (j = 0; j < 10; ++j) {
                        uint32_t *write_p = (uint32_t*)&ptr[j*100];
                        *write_p += i+j;
                }
        }
        return true;
}

RMWAction::RMWAction(uint64_t seed) : Action()
{
        __total = seed;
}

void RMWAction::DoReads()
{
        uint32_t num_fields, num_reads, num_writes, i, j;
        uint64_t *field_ptr, counter;
        counter = 0;
        num_fields = recordSize/sizeof(uint64_t);
        num_reads = __readset.size();
        num_writes = __writeset.size();
        for (i = 0; i < num_reads; ++i) {
                field_ptr = (uint64_t*)Read(i);
                for (j = 0; j < num_fields; ++j)
                        counter += field_ptr[j];
        }
        for (i = 0; i < num_writes; ++i) {
                if (__writeset[i].is_rmw == true) {
                        field_ptr = (uint64_t*)ReadWrite(i);
                        for (j = 0; j < num_fields; ++j)
                                counter += field_ptr[j];
                }
        }
}

void RMWAction::AccumulateValues()
{
        /*
        uint32_t i, num_fields;
        num_fields = recordSize/sizeof(uint64_t);
        __total = 0;
        for (i = 0; i < num_fields; ++i)
                __total += __accumulated[i];
        */

}

void RMWAction::DoWrites()
{
        uint32_t i, j, num_writes, num_fields;
        char *field_ptr;
        num_fields = 10;
        num_writes = __writeset.size();
        for (i = 0; i < num_writes; ++i) {
                assert(__writeset[i].is_rmw == true);
                memcpy(GetWriteRef(i), ReadWrite(i), 1000);
                field_ptr = (char*)GetWriteRef(i);
                for (j = 0; j < num_fields; ++j) {
                        *((uint32_t*)&field_ptr[j*100]) += j+1;
                }
        }
}

bool RMWAction::Run()
{
        uint32_t i, j, num_reads, num_writes, num_fields;
        assert(recordSize == 1000);
        num_reads = __readset.size();
        num_writes = __writeset.size();
        num_fields = YCSB_RECORD_SIZE / 100;
        uint64_t counter = 0;
        for (i = 0; i < num_reads; ++i) {
                char *field_ptr = (char*)Read(i);
                if (SMALL_RECORDS) {
                        counter += *((uint64_t*)field_ptr);
                } else {
                        for (j = 0; j < num_fields; ++j)
                                counter += *((uint64_t*)&field_ptr[j*100]);
                }
        }
        for (i = 0; i < num_writes; ++i) {
                if (__writeset[i].is_rmw) {
                        char *field_ptr = (char*)ReadWrite(i);
                        if (SMALL_RECORDS) {
                                counter += *((uint64_t*)field_ptr);
                        } else {
                                for (j = 0; j < num_fields; ++j)
                                        counter += *((uint64_t*)&field_ptr[j*100]);
                        }
                }
        }

        for (i = 0; i < num_writes; ++i) {
                assert(__writeset[i].is_rmw);
                char *read_ptr = (char*)ReadWrite(i);
                char *write_ptr = (char*)GetWriteRef(i);
                if (SMALL_RECORDS) {
                        *((uint64_t*)write_ptr) =
                                counter + *((uint64_t*)read_ptr);
                } else {
                        memcpy(write_ptr, read_ptr, YCSB_RECORD_SIZE);
                        for (j = 0; j < num_fields; ++j)
                                *((uint64_t*)&write_ptr[j*100]) += j+1+counter;
                        //              *((uint64_t*)&read_ptr[j*100]);
                }
        }

        return true;
}

mv_action::mv_action(txn *t) : translator(t)
{
        this->__version = 0;
        this->__combinedHash = 0;
        this->__readonly = false;
        this->__state = STICKY;
        for (uint32_t i = 0; i < NUM_CC_THREADS; ++i) {
                this->__write_starts.push_back(-1);
                this->__read_starts.push_back(-1);
        }
        this->init = false;
}

bool mv_action::initialized()
{
        return init;
}

static struct big_key get_key(CompositeKey k)
{
        struct big_key ret;
        ret.key = k.key;
        ret.table_id = k.tableId;
        return ret;
}

/*
 * Assumes that all the entries in the transaction's read- and write-sets are
 * initialized.
 */
void mv_action::setup_reverse_index()
{
        uint32_t num_reads, num_writes, i;
        struct big_key key;
        struct key_index index;

        assert(init == false);
        num_reads = __readset.size();
        num_writes = __writeset.size();

        /* Initialize reverse index to items in the write-set. */
        for (i = 0; i < num_writes; ++i) {
                key = get_key(__writeset[i]);
                index.index = i;
                index.initialized = false;
                if (__writeset[i].is_rmw == true)
                        index.use = RMW;
                else
                        index.use = WRITE;
                assert(reverse_index.count(key) == 0);
                reverse_index[key] = index;
        }

        /* Initialize reverse index to items in the read-set. */
        for (i = 0; i < num_reads; ++i) {
                key = get_key(__readset[i]);
                index.index = i;
                index.initialized = true;
                index.use = READ;
                assert(reverse_index.count(key) == 0);
                reverse_index[key] = index;
        }

        /* Optimize read-only txns in the execution phase. */
        if (num_writes == 0)
                __readonly = true;
        else
                __readonly = false;
        init = true;
}

bool mv_action::Run()
{
        return t->Run();
}

void* mv_action::write_ref(uint64_t key, uint32_t table_id)
{
        //        struct big_key bkey;
        //        struct key_index index;
        uint32_t num_writes, i;

        assert(init == true);
        num_writes = this->__writeset.size();
        //        assert(num_writes < 10);
        for (i = 0; i < num_writes; ++i) {
                if (this->__writeset[i].key == key &&
                    this->__writeset[i].tableId == table_id) {
                        assert(!this->writeset[i].is_rmw ||
                               this->__writeset[i].initialized == true);
                        return this->__writeset[i].value->value;
                }
        }
        assert(false);
        return NULL;
}

void* mv_action::read(uint64_t key, uint32_t table_id)
{
        //        struct big_key bkey;
        //        struct key_index index;
        MVRecord *record, *snapshot;
        uint32_t i, num_reads;
        void *ret;

        assert(init == true);
        record = NULL;
        num_reads = this->__readset.size();
        //        assert(num_reads < 10);
        for (i = 0; i < num_reads; ++i) {
                if (this->__readset[i].key == key &&
                    this->__readset[i].tableId == table_id) {
                        record = this->__readset[i].value;
                        break;
                }
        }
        assert(record != NULL);
        if (this->__readonly == true &&
            (
             GET_MV_EPOCH(this->__version) ==
             GET_MV_EPOCH(record->createTimestamp)
             )) {
                snapshot = record->epoch_ancestor;
                ret = (void*)snapshot->value;
        } else {
                ret = (void*)record->value;
        }
        return ret;

        /*
        bkey.key = key;
        bkey.table_id = table_id;
        assert(reverse_index.count(bkey) == 1);
        index = reverse_index[bkey];
        assert(index.use == READ || index.use == RMW);
        if (index.use == READ) {
                record = __readset[index.index].value;
                if (__readonly == true &&
                    (
                     GET_MV_EPOCH(__version) ==
                     GET_MV_EPOCH(record->createTimestamp)
                     )) {
                        snapshot = record->epoch_ancestor;
                        ret = (void*)snapshot->value;
                } else {
                        ret = (void*)__readset[index.index].value->value;
                }
        } else {	// index.use == RMW
                assert(__readonly == false);
                ret = __writeset[index.index].value->value;
        }
        return ret;
        */
}

CompositeKey mv_action::GenerateKey(bool is_rmw, uint32_t tableId, uint64_t key)
{
        CompositeKey toAdd(is_rmw, tableId, key);
        uint32_t threadId =
                CompositeKey::HashKey(&toAdd) % NUM_CC_THREADS;
        toAdd.threadId = threadId;
        this->__combinedHash |= ((uint64_t)1) << threadId;
        return toAdd;
}

void mv_action::add_read_key(uint32_t tableId, uint64_t key)
{
        CompositeKey to_add;
        assert(tableId == 0 || tableId == 1);
        to_add = GenerateKey(false, tableId, key);
        __readset.push_back(to_add);
}

void mv_action::add_write_key(uint32_t tableId, uint64_t key, bool is_rmw)
{
        CompositeKey to_add;
        assert(tableId == 0 || tableId == 1);
        to_add = GenerateKey(is_rmw, tableId, key);
        __writeset.push_back(to_add);
        __readonly = false;
}
<|endoftext|>


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

#include <gtest/gtest.h>
#include <libnode/node.h>
#include <libnode/dgram.h>

#include <libj/json.h>
#include <libj/console.h>

#include <assert.h>

namespace libj {
namespace node {

static const UInt NUM_SEND = 7;

class GTestDgramOnMessage : LIBJ_JS_FUNCTION(GTestDgramOnMessage)
 public:
    GTestDgramOnMessage(dgram::Socket::Ptr srv)
        : bufs_(JsArray::create())
        , rinfos_(JsArray::create())
        , server_(&(*srv)) {}

    virtual Value operator()(JsArray::Ptr args) {
        bufs_->add(args->get(0));
        rinfos_->add(args->get(1));
        if (bufs_->length() >= NUM_SEND) {
            server_->close();
        }
        return Status::OK;
    }

    JsArray::Ptr messages() {
        return bufs_;
    }

    JsArray::Ptr rinfos() {
        return rinfos_;
    }

 private:
    JsArray::Ptr bufs_;
    JsArray::Ptr rinfos_;
    dgram::Socket* server_;
};

class GTestDgramAfterSend : LIBJ_JS_FUNCTION(GTestDgramAfterSend)
 public:
    GTestDgramAfterSend(dgram::Socket::Ptr cli)
        : count_(0)
        , client_(&(*cli)) {}

    virtual Value operator()(JsArray::Ptr args) {
        assert(!args->getCPtr<libj::Error>(0));
        assert(to<Size>(args->get(1)));
        count_++;
        if (count_ >= NUM_SEND) {
            client_->close();
        }
        return Status::OK;
    }

 private:
    UInt count_;
    dgram::Socket* client_;
};

TEST(GTestDgram, TestDgramSend) {
    Int port = 41234;
    dgram::Socket::Ptr server = dgram::createSocket(dgram::Socket::UDP4);

    GTestDgramOnMessage::Ptr onMessage(new GTestDgramOnMessage(server));
    server->on(dgram::Socket::EVENT_MESSAGE, onMessage);
    server->bind(port);

    String::CPtr hello = String::create("hello");
    String::CPtr localhost = String::create("localhost");
    dgram::Socket::Ptr client = dgram::createSocket(dgram::Socket::UDP4);
    GTestDgramAfterSend::Ptr afterSend(new GTestDgramAfterSend(client));
    for (Size i = 0; i < NUM_SEND; i++) {
        Buffer::Ptr msg = Buffer::create(hello);
        client->send(msg, 0, msg->length(), port, localhost, afterSend);
    }

    node::run();

    String::CPtr address = String::create("address");
    String::CPtr localIP = String::create("127.0.0.1");
    JsArray::CPtr messages = onMessage->messages();
    JsArray::CPtr rinfos = onMessage->rinfos();
    Size numMsgs = messages->length();
    ASSERT_EQ(NUM_SEND, numMsgs);
    for (Size i = 0; i < numMsgs; i++) {
        console::printf(console::LEVEL_INFO, ".");

        String::CPtr msg = messages->getCPtr<Buffer>(i)->toString();
        ASSERT_TRUE(msg->equals(hello));

        JsObject::CPtr rinfo = rinfos->getCPtr<JsObject>(i);
        ASSERT_TRUE(rinfo->getCPtr<String>(address)->equals(localIP));
    }
    console::printf(console::LEVEL_INFO, "\n");
}

}  // namespace node
}  // namespace libj
<commit_msg>check the count of sending<commit_after>// Copyright (c) 2013 Plenluno All rights reserved.

#include <gtest/gtest.h>
#include <libnode/node.h>
#include <libnode/dgram.h>

#include <libj/json.h>
#include <libj/console.h>

#include <assert.h>

namespace libj {
namespace node {

static const UInt NUM_SEND = 7;

class GTestDgramOnMessage : LIBJ_JS_FUNCTION(GTestDgramOnMessage)
 public:
    GTestDgramOnMessage(dgram::Socket::Ptr srv)
        : bufs_(JsArray::create())
        , rinfos_(JsArray::create())
        , server_(&(*srv)) {}

    virtual Value operator()(JsArray::Ptr args) {
        bufs_->add(args->get(0));
        rinfos_->add(args->get(1));
        if (bufs_->length() >= NUM_SEND) {
            server_->close();
        }
        return Status::OK;
    }

    JsArray::Ptr messages() {
        return bufs_;
    }

    JsArray::Ptr rinfos() {
        return rinfos_;
    }

 private:
    JsArray::Ptr bufs_;
    JsArray::Ptr rinfos_;
    dgram::Socket* server_;
};

class GTestDgramAfterSend : LIBJ_JS_FUNCTION(GTestDgramAfterSend)
 public:
    GTestDgramAfterSend(dgram::Socket::Ptr cli)
        : count_(0)
        , client_(&(*cli)) {}

    UInt count() { return count_; }

    virtual Value operator()(JsArray::Ptr args) {
        assert(!args->getCPtr<libj::Error>(0));
        assert(to<Size>(args->get(1)));
        count_++;
        if (count_ >= NUM_SEND) {
            client_->close();
        }
        return Status::OK;
    }

 private:
    UInt count_;
    dgram::Socket* client_;
};

TEST(GTestDgram, TestDgramSend) {
    Int port = 41234;
    dgram::Socket::Ptr server = dgram::createSocket(dgram::Socket::UDP4);

    GTestDgramOnMessage::Ptr onMessage(new GTestDgramOnMessage(server));
    server->on(dgram::Socket::EVENT_MESSAGE, onMessage);
    server->bind(port);

    String::CPtr hello = String::create("hello");
    String::CPtr localhost = String::create("localhost");
    dgram::Socket::Ptr client = dgram::createSocket(dgram::Socket::UDP4);
    GTestDgramAfterSend::Ptr afterSend(new GTestDgramAfterSend(client));
    for (Size i = 0; i < NUM_SEND; i++) {
        Buffer::Ptr msg = Buffer::create(hello);
        client->send(msg, 0, msg->length(), port, localhost, afterSend);
    }

    node::run();

    String::CPtr address = String::create("address");
    String::CPtr localIP = String::create("127.0.0.1");
    JsArray::CPtr messages = onMessage->messages();
    JsArray::CPtr rinfos = onMessage->rinfos();
    Size numMsgs = messages->length();
    ASSERT_EQ(NUM_SEND, numMsgs);
    ASSERT_EQ(NUM_SEND, afterSend->count());
    for (Size i = 0; i < numMsgs; i++) {
        console::printf(console::LEVEL_INFO, ".");

        String::CPtr msg = messages->getCPtr<Buffer>(i)->toString();
        ASSERT_TRUE(msg->equals(hello));

        JsObject::CPtr rinfo = rinfos->getCPtr<JsObject>(i);
        ASSERT_TRUE(rinfo->getCPtr<String>(address)->equals(localIP));
    }
    console::printf(console::LEVEL_INFO, "\n");
}

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


<commit_before>/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2018  Serge Bazanski <q3k@symbioticeda.com>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include <cstdio>
#include <math.h>

#include <QApplication>
#include <QCoreApplication>
#include <QMouseEvent>
#include <QWidget>

#include "fpgaviewwidget.h"
#include "log.h"
#include "mainwindow.h"

NEXTPNR_NAMESPACE_BEGIN

void PolyLine::buildPoint(LineShaderData *building, const QVector2D *prev,
                          const QVector2D *cur, const QVector2D *next) const
{
    // buildPoint emits two vertices per line point, along with normals to move
    // them the right directio when rendering and miter to compensate for
    // bends.

    if (cur == nullptr) {
        // BUG
        return;
    }

    if (prev == nullptr && next == nullptr) {
        // BUG
        return;
    }

    // TODO(q3k): fast path for vertical/horizontal lines?

    // TODO(q3k): consider moving some of the linear algebra to the GPU,
    // they're better at this than poor old CPUs.

    // Build two unit vectors pointing in the direction of the two segments
    // defined by (prev, cur) and (cur, next)
    QVector2D dprev, dnext;
    if (prev == nullptr) {
        dnext = *next - *cur;
        dprev = dnext;
    } else if (next == nullptr) {
        dprev = *cur - *prev;
        dnext = dprev;
    } else {
        dprev = *cur - *prev;
        dnext = *next - *cur;
    }
    dprev.normalize();
    dnext.normalize();

    // Calculate tangent unit vector.
    QVector2D tangent(dprev + dnext);
    tangent.normalize();

    // Calculate normal to tangent - this is the line on which the vectors need
    // to be pushed to build a thickened line.
    QVector2D tangent_normal = QVector2D(-tangent.y(), tangent.x());

    // Calculate normal to one of the lines.
    QVector2D dprev_normal = QVector2D(-dprev.y(), dprev.x());
    // https://people.eecs.berkeley.edu/~sequin/CS184/IMGS/Sweep_PolyLine.jpg
    // (the ^-1 is performed in the shader)
    float miter = QVector2D::dotProduct(tangent_normal, dprev_normal);

    float x = cur->x();
    float y = cur->y();
    float mx = tangent_normal.x();
    float my = tangent_normal.y();

    // Push back 'left' vertex.
    building->vertices.push_back(Vertex2DPOD(x, y));
    building->normals.push_back(Vertex2DPOD(mx, my));
    building->miters.push_back(miter);

    // Push back 'right' vertex.
    building->vertices.push_back(Vertex2DPOD(x, y));
    building->normals.push_back(Vertex2DPOD(mx, my));
    building->miters.push_back(-miter);
}

void PolyLine::build(LineShaderData &target) const
{
    if (points_.size() < 2) {
        return;
    }
    const QVector2D *first = &points_.front();
    const QVector2D *last = &points_.back();

    // Index number of vertices, used to build the index buffer.
    unsigned int startIndex = target.vertices.size();
    unsigned int index = startIndex;

    // For every point on the line, call buildPoint with (prev, point, next).
    // If we're building a closed line, prev/next wrap around. Otherwise
    // they are passed as nullptr and buildPoint interprets that accordinglu.
    const QVector2D *prev = nullptr;

    // Loop iterator used to ensure next is valid.
    unsigned int i = 0;
    for (const QVector2D &point : points_) {
        const QVector2D *next = nullptr;
        if (++i < points_.size()) {
            next = (&point + 1);
        }

        // If the line is closed, wrap around. Otherwise, pass nullptr.
        if (prev == nullptr && closed_) {
            buildPoint(&target, last, &point, next);
        } else if (next == nullptr && closed_) {
            buildPoint(&target, prev, &point, first);
        } else {
            buildPoint(&target, prev, &point, next);
        }

        // If we have a prev point relative to cur, build a pair of triangles
        // to render vertices into lines.
        if (prev != nullptr) {
            target.indices.push_back(index);
            target.indices.push_back(index + 1);
            target.indices.push_back(index + 2);

            target.indices.push_back(index + 2);
            target.indices.push_back(index + 1);
            target.indices.push_back(index + 3);

            index += 2;
        }
        prev = &point;
    }

    // If we're closed, built to more vertices that loop the line around.
    if (closed_) {
        target.indices.push_back(index);
        target.indices.push_back(index + 1);
        target.indices.push_back(startIndex);

        target.indices.push_back(startIndex);
        target.indices.push_back(index + 1);
        target.indices.push_back(startIndex + 1);
    }
}

bool LineShader::compile(void)
{
    program_ = new QOpenGLShaderProgram(parent_);
    program_->addShaderFromSourceCode(QOpenGLShader::Vertex,
                                      vertexShaderSource_);
    program_->addShaderFromSourceCode(QOpenGLShader::Fragment,
                                      fragmentShaderSource_);
    if (!program_->link()) {
        printf("could not link program: %s\n",
               program_->log().toStdString().c_str());
        return false;
    }
    attributes_.position = program_->attributeLocation("position");
    attributes_.normal = program_->attributeLocation("normal");
    attributes_.miter = program_->attributeLocation("miter");
    uniforms_.thickness = program_->uniformLocation("thickness");
    uniforms_.projection = program_->uniformLocation("projection");
    uniforms_.color = program_->uniformLocation("color");

    return true;
}

void LineShader::draw(const LineShaderData &line, const QMatrix4x4 &projection)
{
    auto gl = QOpenGLContext::currentContext()->functions();
    program_->bind();

    program_->setUniformValue(uniforms_.projection, projection);
    program_->setUniformValue(uniforms_.thickness, line.thickness);
    program_->setUniformValue(uniforms_.color, line.color.r, line.color.g,
                              line.color.b, line.color.a);

    gl->glVertexAttribPointer(attributes_.position, 2, GL_FLOAT, GL_FALSE, 0,
                              &line.vertices[0]);
    gl->glVertexAttribPointer(attributes_.normal, 2, GL_FLOAT, GL_FALSE, 0,
                              &line.normals[0]);
    gl->glVertexAttribPointer(attributes_.miter, 1, GL_FLOAT, GL_FALSE, 0,
                              &line.miters[0]);

    gl->glEnableVertexAttribArray(0);
    gl->glEnableVertexAttribArray(1);
    gl->glEnableVertexAttribArray(2);

    gl->glDrawElements(GL_TRIANGLES, line.indices.size(), GL_UNSIGNED_INT,
                       &line.indices[0]);

    gl->glDisableVertexAttribArray(2);
    gl->glDisableVertexAttribArray(1);
    gl->glDisableVertexAttribArray(0);
    program_->release();
}

FPGAViewWidget::FPGAViewWidget(QWidget *parent)
        : QOpenGLWidget(parent), moveX_(0), moveY_(0), zoom_(10.0f),
          lineShader_(this)
{
    ctx = qobject_cast<BaseMainWindow *>(getMainWindow())->getContext();
}

QMainWindow *FPGAViewWidget::getMainWindow()
{
    QWidgetList widgets = qApp->topLevelWidgets();
    for (QWidgetList::iterator i = widgets.begin(); i != widgets.end(); ++i)
        if ((*i)->objectName() == "BaseMainWindow")
            return (QMainWindow *)(*i);
    return NULL;
}

FPGAViewWidget::~FPGAViewWidget() {}

QSize FPGAViewWidget::minimumSizeHint() const { return QSize(640, 480); }

QSize FPGAViewWidget::sizeHint() const { return QSize(640, 480); }

void FPGAViewWidget::setXTranslation(float t_x)
{
    if (t_x == moveX_)
        return;

    moveX_ = t_x;
    update();
}

void FPGAViewWidget::setYTranslation(float t_y)
{
    if (t_y == moveY_)
        return;

    moveY_ = t_y;
    update();
}

void FPGAViewWidget::setZoom(float t_z)
{
    if (t_z == zoom_)
        return;
    zoom_ = t_z;

    if (zoom_ < 1.0f)
        zoom_ = 1.0f;
    if (zoom_ > 100.f)
        zoom_ = 100.0f;

    update();
}

void FPGAViewWidget::initializeGL()
{
    if (!lineShader_.compile()) {
        log_error("Could not compile shader.\n");
    }
    initializeOpenGLFunctions();
    glClearColor(1.0, 1.0, 1.0, 0.0);
}

void FPGAViewWidget::drawElement(LineShaderData &out, const GraphicElement &el)
{
    float scale = 1.0, offset = 0.0;

    if (el.type == GraphicElement::G_BOX) {
        auto line = PolyLine(true);
        line.point(offset + scale * el.x1, offset + scale * el.y1);
        line.point(offset + scale * el.x2, offset + scale * el.y1);
        line.point(offset + scale * el.x2, offset + scale * el.y2);
        line.point(offset + scale * el.x1, offset + scale * el.y2);
        line.build(out);
    }

    if (el.type == GraphicElement::G_LINE) {
        PolyLine(offset + scale * el.x1, offset + scale * el.y1,
                 offset + scale * el.x2, offset + scale * el.y2)
                .build(out);
    }
}

void FPGAViewWidget::paintGL()
{
    auto gl = QOpenGLContext::currentContext()->functions();
    const qreal retinaScale = devicePixelRatio();
    gl->glViewport(0, 0, width() * retinaScale, height() * retinaScale);
    gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    float aspect = float(width()) / float(height());

    QMatrix4x4 matrix;
    matrix.ortho(QRectF(-aspect / 2.0, -0.5, aspect, 1.0f));
    matrix.translate(moveX_, moveY_, -0.5);
    matrix.scale(zoom_ * 0.01f, zoom_ * 0.01f, 0);

    // Draw grid.
    auto grid = LineShaderData(0.01f, QColor("#DDD"));
    for (float i = -100.0f; i < 100.0f; i += 1.0f) {
        PolyLine(-100.0f, i, 100.0f, i).build(grid);
        PolyLine(i, -100.0f, i, 100.0f).build(grid);
    }
    lineShader_.draw(grid, matrix);

    // Draw Bels.
    auto bels = LineShaderData(0.02f, QColor("#b000ba"));
    for (auto bel : ctx->getBels()) {
        for (auto &el : ctx->getBelGraphics(bel))
            drawElement(bels, el);
    }
    lineShader_.draw(bels, matrix);

    // Draw Frame Graphics.
    auto frames = LineShaderData(0.02f, QColor("#0066ba"));
    for (auto &el : ctx->getFrameGraphics()) {
        drawElement(frames, el);
    }
    lineShader_.draw(frames, matrix);
}

void FPGAViewWidget::resizeGL(int width, int height) {}

void FPGAViewWidget::mousePressEvent(QMouseEvent *event)
{
    startDragX_ = moveX_;
    startDragY_ = moveY_;
    lastPos_ = event->pos();
}

void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)
{
    const int dx = event->x() - lastPos_.x();
    const int dy = event->y() - lastPos_.y();

    const qreal retinaScale = devicePixelRatio();
    float aspect = float(width()) / float(height());
    const float dx_scale = dx * (1 / (float)width() * retinaScale * aspect);
    const float dy_scale = dy * (1 / (float)height() * retinaScale);

    float xpos = dx_scale + startDragX_;
    float ypos = dy_scale + startDragY_;

    setXTranslation(xpos);
    setYTranslation(ypos);
}

void FPGAViewWidget::wheelEvent(QWheelEvent *event)
{
    QPoint degree = event->angleDelta() / 8;

    if (!degree.isNull()) {
        float steps = degree.y() / 15.0;
        setZoom(zoom_ + steps);
    }
}

NEXTPNR_NAMESPACE_END
<commit_msg>Small fixes.<commit_after>/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2018  Serge Bazanski <q3k@symbioticeda.com>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include <cstdio>
#include <math.h>

#include <QApplication>
#include <QCoreApplication>
#include <QMouseEvent>
#include <QWidget>

#include "fpgaviewwidget.h"
#include "log.h"
#include "mainwindow.h"

NEXTPNR_NAMESPACE_BEGIN

void PolyLine::buildPoint(LineShaderData *building, const QVector2D *prev,
                          const QVector2D *cur, const QVector2D *next) const
{
    // buildPoint emits two vertices per line point, along with normals to move
    // them the right directio when rendering and miter to compensate for
    // bends.

    if (cur == nullptr) {
        // BUG
        return;
    }

    if (prev == nullptr && next == nullptr) {
        // BUG
        return;
    }

    // TODO(q3k): fast path for vertical/horizontal lines?

    // TODO(q3k): consider moving some of the linear algebra to the GPU,
    // they're better at this than poor old CPUs.

    // Build two unit vectors pointing in the direction of the two segments
    // defined by (prev, cur) and (cur, next)
    QVector2D dprev, dnext;
    if (prev == nullptr) {
        dnext = *next - *cur;
        dprev = dnext;
    } else if (next == nullptr) {
        dprev = *cur - *prev;
        dnext = dprev;
    } else {
        dprev = *cur - *prev;
        dnext = *next - *cur;
    }
    dprev.normalize();
    dnext.normalize();

    // Calculate tangent unit vector.
    QVector2D tangent(dprev + dnext);
    tangent.normalize();

    // Calculate normal to tangent - this is the line on which the vectors need
    // to be pushed to build a thickened line.
    const QVector2D tangent_normal = QVector2D(-tangent.y(), tangent.x());

    // Calculate normal to one of the lines.
    const QVector2D dprev_normal = QVector2D(-dprev.y(), dprev.x());
    // https://people.eecs.berkeley.edu/~sequin/CS184/IMGS/Sweep_PolyLine.jpg
    // (the ^-1 is performed in the shader)
    const float miter = QVector2D::dotProduct(tangent_normal, dprev_normal);

    const float x = cur->x();
    const float y = cur->y();
    const float mx = tangent_normal.x();
    const float my = tangent_normal.y();

    // Push back 'left' vertex.
    building->vertices.push_back(Vertex2DPOD(x, y));
    building->normals.push_back(Vertex2DPOD(mx, my));
    building->miters.push_back(miter);

    // Push back 'right' vertex.
    building->vertices.push_back(Vertex2DPOD(x, y));
    building->normals.push_back(Vertex2DPOD(mx, my));
    building->miters.push_back(-miter);
}

void PolyLine::build(LineShaderData &target) const
{
    if (points_.size() < 2) {
        return;
    }
    const QVector2D *first = &points_.front();
    const QVector2D *last = &points_.back();

    // Index number of vertices, used to build the index buffer.
    unsigned int startIndex = target.vertices.size();
    unsigned int index = startIndex;

    // For every point on the line, call buildPoint with (prev, point, next).
    // If we're building a closed line, prev/next wrap around. Otherwise
    // they are passed as nullptr and buildPoint interprets that accordinglu.
    const QVector2D *prev = nullptr;

    // Loop iterator used to ensure next is valid.
    unsigned int i = 0;
    for (const QVector2D &point : points_) {
        const QVector2D *next = nullptr;
        if (++i < points_.size()) {
            next = (&point + 1);
        }

        // If the line is closed, wrap around. Otherwise, pass nullptr.
        if (prev == nullptr && closed_) {
            buildPoint(&target, last, &point, next);
        } else if (next == nullptr && closed_) {
            buildPoint(&target, prev, &point, first);
        } else {
            buildPoint(&target, prev, &point, next);
        }

        // If we have a prev point relative to cur, build a pair of triangles
        // to render vertices into lines.
        if (prev != nullptr) {
            target.indices.push_back(index);
            target.indices.push_back(index + 1);
            target.indices.push_back(index + 2);

            target.indices.push_back(index + 2);
            target.indices.push_back(index + 1);
            target.indices.push_back(index + 3);

            index += 2;
        }
        prev = &point;
    }

    // If we're closed, build two more vertices that loop the line around.
    if (closed_) {
        target.indices.push_back(index);
        target.indices.push_back(index + 1);
        target.indices.push_back(startIndex);

        target.indices.push_back(startIndex);
        target.indices.push_back(index + 1);
        target.indices.push_back(startIndex + 1);
    }
}

bool LineShader::compile(void)
{
    program_ = new QOpenGLShaderProgram(parent_);
    program_->addShaderFromSourceCode(QOpenGLShader::Vertex,
                                      vertexShaderSource_);
    program_->addShaderFromSourceCode(QOpenGLShader::Fragment,
                                      fragmentShaderSource_);
    if (!program_->link()) {
        printf("could not link program: %s\n",
               program_->log().toStdString().c_str());
        return false;
    }
    attributes_.position = program_->attributeLocation("position");
    attributes_.normal = program_->attributeLocation("normal");
    attributes_.miter = program_->attributeLocation("miter");
    uniforms_.thickness = program_->uniformLocation("thickness");
    uniforms_.projection = program_->uniformLocation("projection");
    uniforms_.color = program_->uniformLocation("color");

    return true;
}

void LineShader::draw(const LineShaderData &line, const QMatrix4x4 &projection)
{
    auto gl = QOpenGLContext::currentContext()->functions();
    program_->bind();

    program_->setUniformValue(uniforms_.projection, projection);
    program_->setUniformValue(uniforms_.thickness, line.thickness);
    program_->setUniformValue(uniforms_.color, line.color.r, line.color.g,
                              line.color.b, line.color.a);

    gl->glVertexAttribPointer(attributes_.position, 2, GL_FLOAT, GL_FALSE, 0,
                              &line.vertices[0]);
    gl->glVertexAttribPointer(attributes_.normal, 2, GL_FLOAT, GL_FALSE, 0,
                              &line.normals[0]);
    gl->glVertexAttribPointer(attributes_.miter, 1, GL_FLOAT, GL_FALSE, 0,
                              &line.miters[0]);

    gl->glEnableVertexAttribArray(0);
    gl->glEnableVertexAttribArray(1);
    gl->glEnableVertexAttribArray(2);

    gl->glDrawElements(GL_TRIANGLES, line.indices.size(), GL_UNSIGNED_INT,
                       &line.indices[0]);

    gl->glDisableVertexAttribArray(2);
    gl->glDisableVertexAttribArray(1);
    gl->glDisableVertexAttribArray(0);
    program_->release();
}

FPGAViewWidget::FPGAViewWidget(QWidget *parent)
        : QOpenGLWidget(parent), moveX_(0), moveY_(0), zoom_(10.0f),
          lineShader_(this)
{
    ctx = qobject_cast<BaseMainWindow *>(getMainWindow())->getContext();
}

QMainWindow *FPGAViewWidget::getMainWindow()
{
    QWidgetList widgets = qApp->topLevelWidgets();
    for (QWidgetList::iterator i = widgets.begin(); i != widgets.end(); ++i)
        if ((*i)->objectName() == "BaseMainWindow")
            return (QMainWindow *)(*i);
    return NULL;
}

FPGAViewWidget::~FPGAViewWidget() {}

QSize FPGAViewWidget::minimumSizeHint() const { return QSize(640, 480); }

QSize FPGAViewWidget::sizeHint() const { return QSize(640, 480); }

void FPGAViewWidget::setXTranslation(float t_x)
{
    if (t_x == moveX_)
        return;

    moveX_ = t_x;
    update();
}

void FPGAViewWidget::setYTranslation(float t_y)
{
    if (t_y == moveY_)
        return;

    moveY_ = t_y;
    update();
}

void FPGAViewWidget::setZoom(float t_z)
{
    if (t_z == zoom_)
        return;
    zoom_ = t_z;

    if (zoom_ < 1.0f)
        zoom_ = 1.0f;
    if (zoom_ > 100.f)
        zoom_ = 100.0f;

    update();
}

void FPGAViewWidget::initializeGL()
{
    if (!lineShader_.compile()) {
        log_error("Could not compile shader.\n");
    }
    initializeOpenGLFunctions();
    glClearColor(1.0, 1.0, 1.0, 0.0);
}

void FPGAViewWidget::drawElement(LineShaderData &out, const GraphicElement &el)
{
    const float scale = 1.0, offset = 0.0;

    if (el.type == GraphicElement::G_BOX) {
        auto line = PolyLine(true);
        line.point(offset + scale * el.x1, offset + scale * el.y1);
        line.point(offset + scale * el.x2, offset + scale * el.y1);
        line.point(offset + scale * el.x2, offset + scale * el.y2);
        line.point(offset + scale * el.x1, offset + scale * el.y2);
        line.build(out);
    }

    if (el.type == GraphicElement::G_LINE) {
        PolyLine(offset + scale * el.x1, offset + scale * el.y1,
                 offset + scale * el.x2, offset + scale * el.y2)
                .build(out);
    }
}

void FPGAViewWidget::paintGL()
{
    auto gl = QOpenGLContext::currentContext()->functions();
    const qreal retinaScale = devicePixelRatio();
    gl->glViewport(0, 0, width() * retinaScale, height() * retinaScale);
    gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    const float aspect = float(width()) / float(height());

    QMatrix4x4 matrix;
    matrix.ortho(QRectF(-aspect / 2.0, -0.5, aspect, 1.0f));
    matrix.translate(moveX_, moveY_, -0.5);
    matrix.scale(zoom_ * 0.01f, zoom_ * 0.01f, 0);

    // Draw grid.
    auto grid = LineShaderData(0.01f, QColor("#DDD"));
    for (float i = -100.0f; i < 100.0f; i += 1.0f) {
        PolyLine(-100.0f, i, 100.0f, i).build(grid);
        PolyLine(i, -100.0f, i, 100.0f).build(grid);
    }
    lineShader_.draw(grid, matrix);

    // Draw Bels.
    auto bels = LineShaderData(0.02f, QColor("#b000ba"));
    for (auto bel : ctx->getBels()) {
        for (auto &el : ctx->getBelGraphics(bel))
            drawElement(bels, el);
    }
    lineShader_.draw(bels, matrix);

    // Draw Frame Graphics.
    auto frames = LineShaderData(0.02f, QColor("#0066ba"));
    for (auto &el : ctx->getFrameGraphics()) {
        drawElement(frames, el);
    }
    lineShader_.draw(frames, matrix);
}

void FPGAViewWidget::resizeGL(int width, int height) {}

void FPGAViewWidget::mousePressEvent(QMouseEvent *event)
{
    startDragX_ = moveX_;
    startDragY_ = moveY_;
    lastPos_ = event->pos();
}

void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)
{
    const int dx = event->x() - lastPos_.x();
    const int dy = event->y() - lastPos_.y();

    const qreal retinaScale = devicePixelRatio();
    float aspect = float(width()) / float(height());
    const float dx_scale = dx * (1 / (float)width() * retinaScale * aspect);
    const float dy_scale = dy * (1 / (float)height() * retinaScale);

    float xpos = dx_scale + startDragX_;
    float ypos = dy_scale + startDragY_;

    setXTranslation(xpos);
    setYTranslation(ypos);
}

void FPGAViewWidget::wheelEvent(QWheelEvent *event)
{
    QPoint degree = event->angleDelta() / 8;

    if (!degree.isNull()) {
        float steps = degree.y() / 15.0;
        setZoom(zoom_ + steps);
    }
}

NEXTPNR_NAMESPACE_END
<|endoftext|>


<commit_before>
// Implementation of the attribute table (AttrTable) class.
//
// jhrg 7/29/94

// $Log: AttrTable.cc,v $
// Revision 1.6  1994/10/05 16:38:17  jimg
// Changed internal representation of the attribute table from a Map
// to a DLList<>.
//
// Revision 1.5  1994/09/27  22:42:44  jimg
// Changed definition of the class AttrTable; it no longer inherits from
// AttrVHMap, instead it uses that class (contains a member that is an instance
// of AttrVHMap).
// Added mfuncs to AttrTable so that the new member could be set/accessed.
//
// Revision 1.4  1994/09/09  15:26:39  jimg
// Removed operator<< and added print() since I have no good way to define
// operator>>. It seems best to define all operators from a set (like <<, >>)
// or none at all. Since parse() is the input mfunc, it seems that output
// should be a mfunc too.
//
// Revision 1.3  1994/08/02  20:11:27  jimg
// Changes operator<< so that it writes a parsable version of the
// attribute table.
//
// Revision 1.2  1994/08/02  19:17:37  jimg
// Fixed `$Log: AttrTable.cc,v $
// Fixed `Revision 1.6  1994/10/05 16:38:17  jimg
// Fixed `Changed internal representation of the attribute table from a Map
// Fixed `to a DLList<>.
// Fixed `
// Revision 1.5  1994/09/27  22:42:44  jimg
// Changed definition of the class AttrTable; it no longer inherits from
// AttrVHMap, instead it uses that class (contains a member that is an instance
// of AttrVHMap).
// Added mfuncs to AttrTable so that the new member could be set/accessed.
//
// Revision 1.4  1994/09/09  15:26:39  jimg
// Removed operator<< and added print() since I have no good way to define
// operator>>. It seems best to define all operators from a set (like <<, >>)
// or none at all. Since parse() is the input mfunc, it seems that output
// should be a mfunc too.
//
// Revision 1.3  1994/08/02  20:11:27  jimg
// Changes operator<< so that it writes a parsable version of the
// attribute table.
//' comments and rcsid[] variables (syntax errors due to //
// comments caused compilation failures.
// das.tab.c and .h are commited now as well.
//
// Revision 1.1  1994/08/02  18:32:04  jimg
// The implementation of AttrTable. This file defined ostream &operator<< and
// a static class variable String empty (it is initialized to "").
//

static char rcsid[]="$Id: AttrTable.cc,v 1.6 1994/10/05 16:38:17 jimg Exp $";

#ifdef __GNUG__
#pragma implementation
#endif

#include <ostream.h>

#include "AttrTable.h"

AttrTable::AttrTable()
{
}

Pix
AttrTable::first_attr()
{
    return map.first();
}

void
AttrTable::next_attr(Pix &p)
{
    map.next(p);
}

String
AttrTable::get_name(Pix p)
{
    return map(p).name;
}

String
AttrTable::get_type(Pix p)
{
    return map(p).type;
}

String
AttrTable::get_attr(Pix p)
{
    return map(p).attr;
}

// private mfunc that finds the entry with name == target

Pix
AttrTable::find(const String &target)
{
    for (Pix p = map.first(); p; map.next(p))
	if (target == map(p).name)
	    return p;
}

String
AttrTable::get_attr(const String &name)
{
    Pix p = find(name);
    if (p)
	return map(p).attr;
    else
	return (char *)0;
}

String
AttrTable::get_attr(const char *name)
{
    Pix p = find((String)name);
    if (p)
	return map(p).attr;
    else
	return (char *)0;
}

String
AttrTable::get_type(const String &name)
{
    Pix p = find(name);
    if (p)
	return map(p).type;
    else
	return (char *)0;
}

String
AttrTable::get_type(const char *name)
{
    Pix p = find((String)name);
    if (p)
	return map(p).type;
    else
	return (char *)0;
}

void
AttrTable::append_attr(const String &name, String type, String attr)
{
    entry e;

    e.name = name;
    e.type = type;
    e.attr = attr;

    map.append(e);
}

void
AttrTable::del_attr(const String &name)
{
    Pix p = find(name);
    if (p) {
	map.prev(p);
	map.del_after(p);
    }
}

void
AttrTable::print(ostream &os, String pad)
{
    for(Pix p = map.first(); p; map.next(p))
	os << pad << map(p).type << " " << map(p).name << " " << map(p).attr
	   << ";" << endl;
}

<commit_msg>Added a new version of append_attr that takes (const char *)s and modified the version that takes strings to take (const String &).<commit_after>
// Implementation of the attribute table (AttrTable) class.
//
// jhrg 7/29/94

// $Log: AttrTable.cc,v $
// Revision 1.7  1994/10/13 15:43:29  jimg
// Added a new version of append_attr that takes (const char *)s and modified
// the version that takes strings to take (const String &).
//
// Revision 1.6  1994/10/05  16:38:17  jimg
// Changed internal representation of the attribute table from a Map
// to a DLList<>.
//
// Revision 1.5  1994/09/27  22:42:44  jimg
// Changed definition of the class AttrTable; it no longer inherits from
// AttrVHMap, instead it uses that class (contains a member that is an instance
// of AttrVHMap).
// Added mfuncs to AttrTable so that the new member could be set/accessed.
//
// Revision 1.4  1994/09/09  15:26:39  jimg
// Removed operator<< and added print() since I have no good way to define
// operator>>. It seems best to define all operators from a set (like <<, >>)
// or none at all. Since parse() is the input mfunc, it seems that output
// should be a mfunc too.
//
// Revision 1.3  1994/08/02  20:11:27  jimg
// Changes operator<< so that it writes a parsable version of the
// attribute table.
//' comments and rcsid[] variables (syntax errors due to //
// comments caused compilation failures.
// das.tab.c and .h are commited now as well.
//
// Revision 1.1  1994/08/02  18:32:04  jimg
// The implementation of AttrTable. This file defined ostream &operator<< and
// a static class variable String empty (it is initialized to "").
//

static char rcsid[]="$Id: AttrTable.cc,v 1.7 1994/10/13 15:43:29 jimg Exp $";

#ifdef __GNUG__
#pragma implementation
#endif

#include <ostream.h>

#include "AttrTable.h"

AttrTable::AttrTable()
{
}

Pix
AttrTable::first_attr()
{
    return map.first();
}

void
AttrTable::next_attr(Pix &p)
{
    map.next(p);
}

String
AttrTable::get_name(Pix p)
{
    return map(p).name;
}

String
AttrTable::get_type(Pix p)
{
    return map(p).type;
}

String
AttrTable::get_attr(Pix p)
{
    return map(p).attr;
}

// private mfunc that finds the entry with name == target

Pix
AttrTable::find(const String &target)
{
    for (Pix p = map.first(); p; map.next(p))
	if (target == map(p).name)
	    return p;
}

String
AttrTable::get_attr(const String &name)
{
    Pix p = find(name);
    if (p)
	return map(p).attr;
    else
	return (char *)0;
}

String
AttrTable::get_attr(const char *name)
{
    Pix p = find((String)name);
    if (p)
	return map(p).attr;
    else
	return (char *)0;
}

String
AttrTable::get_type(const String &name)
{
    Pix p = find(name);
    if (p)
	return map(p).type;
    else
	return (char *)0;
}

String
AttrTable::get_type(const char *name)
{
    Pix p = find((String)name);
    if (p)
	return map(p).type;
    else
	return (char *)0;
}

// Added this version of append_attr to reduce creation on String temps

void
AttrTable::append_attr(const char *name, const char *type, const char *attr)
{
    entry e;

    e.name = name;
    e.type = type;
    e.attr = attr;

    map.append(e);
}

void
AttrTable::append_attr(const String &name, const String &type,
		       const String &attr)
{
    entry e;

    e.name = name;
    e.type = type;
    e.attr = attr;

    map.append(e);
}

void
AttrTable::del_attr(const String &name)
{
    Pix p = find(name);
    if (p) {
	map.prev(p);
	map.del_after(p);
    }
}

void
AttrTable::print(ostream &os, String pad)
{
    for(Pix p = map.first(); p; map.next(p))
	os << pad << map(p).type << " " << map(p).name << " " << map(p).attr
	   << ";" << endl;
}

<|endoftext|>