LCOV - code coverage report
Current view: top level - src - codec_proxy.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 65 86 75.6 %
Date: 2019-01-29 11:06:41 Functions: 9 16 56.2 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2016-2018  Fulvio Benini
       3             : 
       4             :  * This file is part of Scid (Shane's Chess Information Database).
       5             :  *
       6             :  * Scid is free software: you can redistribute it and/or modify
       7             :  * it under the terms of the GNU General Public License as published by
       8             :  * the Free Software Foundation.
       9             :  *
      10             :  * Scid is distributed in the hope that it will be useful,
      11             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :  * GNU General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License
      16             :  * along with Scid.  If not, see <http://www.gnu.org/licenses/>.
      17             :  *
      18             :  */
      19             : 
      20             : /** @file
      21             :  * Implements the CodecProxy class, which serves as base class for non-native
      22             :  * databases.
      23             :  */
      24             : 
      25             : #ifndef CODEC_PROXY_H
      26             : #define CODEC_PROXY_H
      27             : 
      28             : #include "codec.h"
      29             : #include "codec_memory.h"
      30             : #include "common.h"
      31             : #include "game.h"
      32             : 
      33             : #ifndef MULTITHREADING_OFF
      34             : #include <atomic>
      35             : #include <thread>
      36             : #endif
      37             : 
      38             : /**
      39             :  * Base class for non-native databases.
      40             :  * Every class derived from ICodecDatabase must keep an @e Index object and the
      41             :  * corresponding @e NameBase object fully updated in memory.
      42             :  * This implies that the virtual function dyn_open() must load in memory the
      43             :  * header's data of all the games; however a dependency between the codecs and
      44             :  * the @e Index class is not desirable.
      45             :  * This class provides an interface that encapsulates the codecs, requiring only
      46             :  * the ability to exchange @e Game objects.
      47             :  */
      48          38 : template <typename Derived> class CodecProxy : public CodecMemory {
      49        2036 :         Derived* getDerived() { return static_cast<Derived*>(this); }
      50             : 
      51             : public:
      52             :         /**
      53             :          * Opens/creates a database encoded in a non-native format.
      54             :          * @param filename: full path of the database to be opened.
      55             :          * @param fMode:    valid file access mode.
      56             :          * @returns OK in case of success, an @p errorT code otherwise.
      57             :          */
      58             :         errorT open(const char* filename, fileModeT fMode);
      59             : 
      60             :         /**
      61             :          * Reads the next game.
      62             :          * A derived class implements this function to sequentially read the games
      63             :          * contained into the database.
      64             :          * @param Game&: the Game object where the data will be stored.
      65             :          * @returns
      66             :          * - ERROR_NotFound if there are no more games to be read.
      67             :          * - OK otherwise.
      68             :          */
      69             :         errorT parseNext(Game&) { return ERROR_NotFound; }
      70             : 
      71             :         /**
      72             :          * Returns info about the parsing progress.
      73             :          * @returns a pair<size_t, size_t> where first element is the quantity of
      74             :          * data parsed and second one is the total amount of data of the database.
      75             :          */
      76             :         std::pair<size_t, size_t> parseProgress() {
      77             :                 return std::pair<size_t, size_t>(1, 1);
      78             :         }
      79             : 
      80             :         /**
      81             :          * Returns the list of errors produced by parseNext() calls.
      82             :          */
      83             :         const char* parseErrors() { return NULL; }
      84             : 
      85             :         /**
      86             :          * Adds a game into the database.
      87             :          * @param Game*: valid pointer to a Game object with the new data.
      88             :          * @returns OK in case of success, an @p errorT code otherwise.
      89             :          */
      90             :         errorT gameAdd(Game*) { return ERROR_CodecUnsupFeat; }
      91             : 
      92             :         /**
      93             :          * Replaces a game in the database.
      94             :          * @param Game*:    valid pointer to a Game object with the new data.
      95             :          * @param gamenumT: valid gamenumT of the game to be replaced.
      96             :          * @returns OK in case of success, an @p errorT code otherwise.
      97             :          */
      98           0 :         errorT gameSave(Game*, gamenumT) { return ERROR_CodecUnsupFeat; }
      99             : 
     100             : private:
     101        1000 :         errorT addGame(Game* game) final {
     102        1000 :                 errorT err = getDerived()->gameAdd(game);
     103        1000 :                 if (err != OK)
     104           0 :                         return err;
     105             : 
     106        1000 :                 return CodecMemory::addGame(game);
     107             :         }
     108             : 
     109           0 :         errorT saveGame(Game* game, gamenumT replaced) final {
     110           0 :                 errorT err = getDerived()->gameSave(game, replaced);
     111           0 :                 if (err != OK)
     112           0 :                         return err;
     113             : 
     114           0 :                 return CodecMemory::saveGame(game, replaced);
     115             :         }
     116             : 
     117        1000 :         errorT addGame(const IndexEntry* srcIe, const NameBase* srcNb,
     118             :                        const byte* srcData, size_t dataLen) final {
     119        2000 :                 ByteBuffer buf(0);
     120        1000 :                 buf.ProvideExternal(const_cast<byte*>(srcData), dataLen);
     121        2000 :                 Game game;
     122        1000 :                 errorT err = game.Decode(&buf, GAME_DECODE_ALL);
     123        1000 :                 if (err == OK)
     124        1000 :                         err = game.LoadStandardTags(srcIe, srcNb);
     125        1000 :                 if (err != OK)
     126           0 :                         return err;
     127             : 
     128        1000 :                 err = getDerived()->gameAdd(&game);
     129        1000 :                 if (err != OK)
     130           0 :                         return err;
     131             : 
     132        1000 :                 return CodecMemory::addGame(srcIe, srcNb, srcData, dataLen);
     133             :         }
     134             : 
     135           0 :         errorT saveIndexEntry(const IndexEntry&, gamenumT) final {
     136           0 :                 return ERROR_CodecUnsupFeat;
     137             :         }
     138             : 
     139           0 :         std::pair<errorT, idNumberT> addName(nameT, const char*) final {
     140           0 :                 return std::pair<errorT, idNumberT>(ERROR_CodecUnsupFeat, 0);
     141             :         }
     142             : 
     143             :         /*
     144             :          * Create a memory database, open the non-native database @p filename and
     145             :          * copy all the games into the memory database.
     146             :          */
     147          19 :         errorT dyn_open(fileModeT fMode, const char* filename,
     148             :                         const Progress& progress, Index* idx, NameBase* nb) final {
     149          19 :                 if (filename == 0)
     150           0 :                         return ERROR;
     151             : 
     152          19 :                 errorT err = CodecMemory::dyn_open(FMODE_Memory, filename, progress,
     153             :                                                    idx, nb);
     154          19 :                 if (err != OK)
     155           0 :                         return err;
     156             : 
     157          19 :                 err = getDerived()->open(filename, fMode);
     158          19 :                 if (err != OK)
     159           2 :                         return err;
     160             : 
     161        2023 :                 return parseGames(progress, *getDerived(), [&](Game& game) {
     162        2006 :                         return this->CodecMemory::addGame(&game);
     163        2023 :                 });
     164             :         }
     165             : 
     166             : public:
     167             :         /*
     168             :          * Given a source database of type CodecProxy<T>, for each game a
     169             :          * corresponding Game object is created and dispatched to @e destFn.
     170             :          */
     171             :         template <typename TProgress, typename TSource, typename TDestFn>
     172          17 :         static errorT parseGames(const TProgress& progress, TSource& src,
     173             :                                  TDestFn destFn) {
     174             : #ifndef MULTITHREADING_OFF
     175          17 :                 auto workTotal = src.parseProgress().second;
     176             : 
     177          34 :                 Game game[4];
     178          17 :                 std::atomic<size_t> workDone{};
     179          17 :                 std::atomic<int8_t> sync[4] = {};
     180             :                 enum {sy_free, sy_used, sy_stop};
     181             : 
     182          51 :                 std::thread producer([&]() {
     183             :                         uint64_t slot;
     184          17 :                         uint64_t nProduced = 0;
     185        2006 :                         while (true) {
     186        2023 :                                 slot = nProduced % 4;
     187             :                                 int sy;
     188             :                                 while (true) { // spinlock if the slot is in use
     189        6090 :                                         sy = sync[slot].load(std::memory_order_acquire);
     190        2030 :                                         if (sy == sy_used)
     191           7 :                                                 std::this_thread::yield();
     192             :                                         else
     193        2023 :                                                 break;
     194             :                                 };
     195        2023 :                                 if (sy == sy_stop)
     196           0 :                                         break;
     197             : 
     198        2023 :                                 if (src.parseNext(game[slot]) == ERROR_NotFound)
     199          17 :                                         break;
     200             : 
     201        2006 :                                 if (++nProduced % 1024 == 0) {
     202           0 :                                         workDone.store(src.parseProgress().first,
     203             :                                                        std::memory_order_release);
     204             :                                 }
     205             : 
     206        2006 :                                 sync[slot].store(sy_used, std::memory_order_release);
     207             :                         }
     208          17 :                         sync[slot].store(sy_stop, std::memory_order_release);
     209          17 :                 });
     210             : 
     211             :                 // Consumer
     212          17 :                 errorT err = OK;
     213             :                 uint64_t slot;
     214          17 :                 uint64_t nImported = 0;
     215        2006 :                 while (true) {
     216        2023 :                         slot = nImported % 4;
     217             :                         int sy;
     218             :                         while (true) { // spinlock if the slot is empty
     219    27262862 :                                 sy = sync[slot].load(std::memory_order_acquire);
     220     9088295 :                                 if (sy == sy_free)
     221     9086272 :                                         std::this_thread::yield();
     222             :                                 else
     223        2023 :                                         break;
     224             :                         };
     225        2023 :                         if (sy == sy_stop)
     226          17 :                                 break;
     227             : 
     228        2006 :                         if (++nImported % 1024 == 0) {
     229           0 :                                 if (!progress.report(workDone.load(std::memory_order_acquire),
     230             :                                                      workTotal)) {
     231           0 :                                         err = ERROR_UserCancel;
     232           0 :                                         break;
     233             :                                 }
     234             :                         }
     235             : 
     236        2006 :                         err = destFn(game[slot]);
     237        2006 :                         if (err != OK)
     238           0 :                                 break;
     239             : 
     240        2006 :                         sync[slot].store(sy_free, std::memory_order_release);
     241             :                 }
     242          17 :                 sync[slot].store(sy_stop, std::memory_order_release);
     243             : 
     244          17 :                 producer.join();
     245          17 :                 progress(1, 1, src.parseErrors());
     246          34 :                 return err;
     247             : 
     248             : #else
     249             :                 Game g;
     250             :                 errorT err = OK;
     251             :                 uint64_t nImported = 0;
     252             :                 while (src.parseNext(g) != ERROR_NotFound) {
     253             :                         err = destFn(g);
     254             :                         if (err != OK)
     255             :                                 break;
     256             : 
     257             :                         if (++nImported % 1024 == 0) {
     258             :                                 std::pair<size_t, size_t> count = src.parseProgress();
     259             :                                 if (!progress.report(count.first, count.second)) {
     260             :                                         err = ERROR_UserCancel;
     261             :                                         break;
     262             :                                 }
     263             :                         }
     264             :                 }
     265             :                 progress(1, 1, src.parseErrors());
     266             :                 return err;
     267             : #endif
     268             :         }
     269             : };
     270             : 
     271             : #endif

Generated by: LCOV version 1.13