1 /**
2  * Orthanc - A Lightweight, RESTful DICOM Store
3  * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4  * Department, University Hospital of Liege, Belgium
5  * Copyright (C) 2017-2021 Osimis S.A., Belgium
6  *
7  * This program is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * as published by the Free Software Foundation, either version 3 of
10  * the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program. If not, see
19  * <http://www.gnu.org/licenses/>.
20  **/
21 
22 
23 #include "../PrecompiledHeaders.h"
24 #include "ParsedDicomCache.h"
25 
26 #include "../OrthancException.h"
27 
28 namespace Orthanc
29 {
30   class ParsedDicomCache::Item : public ICacheable
31   {
32   private:
33     std::unique_ptr<ParsedDicomFile>  dicom_;
34     size_t                            fileSize_;
35 
36   public:
Item(ParsedDicomFile * dicom,size_t fileSize)37     Item(ParsedDicomFile* dicom,
38          size_t fileSize) :
39       dicom_(dicom),
40       fileSize_(fileSize)
41     {
42       if (dicom == NULL)
43       {
44         throw OrthancException(ErrorCode_NullPointer);
45       }
46     }
47 
GetMemoryUsage() const48     virtual size_t GetMemoryUsage() const ORTHANC_OVERRIDE
49     {
50       return fileSize_;
51     }
52 
GetDicom() const53     ParsedDicomFile& GetDicom() const
54     {
55       assert(dicom_.get() != NULL);
56       return *dicom_;
57     }
58   };
59 
60 
ParsedDicomCache(size_t size)61   ParsedDicomCache::ParsedDicomCache(size_t size) :
62     cacheSize_(size),
63     largeSize_(0)
64   {
65     if (size == 0)
66     {
67       throw OrthancException(ErrorCode_ParameterOutOfRange);
68     }
69   }
70 
71 
GetNumberOfItems()72   size_t ParsedDicomCache::GetNumberOfItems()
73   {
74 #if !defined(__EMSCRIPTEN__)
75     boost::mutex::scoped_lock lock(mutex_);
76 #endif
77 
78     if (cache_.get() == NULL)
79     {
80       return (largeDicom_.get() == NULL ? 0 : 1);
81     }
82     else
83     {
84       assert(largeDicom_.get() == NULL);
85       assert(largeSize_ == 0);
86       return cache_->GetNumberOfItems();
87     }
88   }
89 
90 
GetCurrentSize()91   size_t ParsedDicomCache::GetCurrentSize()
92   {
93 #if !defined(__EMSCRIPTEN__)
94     boost::mutex::scoped_lock lock(mutex_);
95 #endif
96 
97     if (cache_.get() == NULL)
98     {
99       return largeSize_;
100     }
101     else
102     {
103       assert(largeDicom_.get() == NULL);
104       assert(largeSize_ == 0);
105       return cache_->GetCurrentSize();
106     }
107   }
108 
109 
Invalidate(const std::string & id)110   void ParsedDicomCache::Invalidate(const std::string& id)
111   {
112 #if !defined(__EMSCRIPTEN__)
113     boost::mutex::scoped_lock lock(mutex_);
114 #endif
115 
116     if (cache_.get() != NULL)
117     {
118       cache_->Invalidate(id);
119     }
120 
121     if (largeId_ == id)
122     {
123       largeDicom_.reset(NULL);
124       largeSize_ = 0;
125     }
126   }
127 
128 
Acquire(const std::string & id,ParsedDicomFile * dicom,size_t fileSize)129   void ParsedDicomCache::Acquire(const std::string& id,
130                                  ParsedDicomFile* dicom,  // Takes ownership
131                                  size_t fileSize)
132   {
133 #if !defined(__EMSCRIPTEN__)
134     boost::mutex::scoped_lock lock(mutex_);
135 #endif
136 
137     if (fileSize >= cacheSize_)
138     {
139       cache_.reset(NULL);
140       largeDicom_.reset(dicom);
141       largeId_ = id;
142       largeSize_ = fileSize;
143     }
144     else
145     {
146       largeDicom_.reset(NULL);
147       largeSize_ = 0;
148 
149       if (cache_.get() == NULL)
150       {
151         cache_.reset(new MemoryObjectCache);
152         cache_->SetMaximumSize(cacheSize_);
153       }
154 
155       cache_->Acquire(id, new Item(dicom, fileSize));
156     }
157   }
158 
159 
Accessor(ParsedDicomCache & that,const std::string & id)160   ParsedDicomCache::Accessor::Accessor(ParsedDicomCache& that,
161                                        const std::string& id) :
162 #if !defined(__EMSCRIPTEN__)
163     lock_(that.mutex_),
164 #endif
165     id_(id),
166     file_(NULL),
167     fileSize_(0)
168   {
169     if (that.largeDicom_.get() != NULL &&
170         that.largeId_ == id)
171     {
172       file_ = that.largeDicom_.get();
173       fileSize_ = that.largeSize_;
174     }
175     else if (that.cache_.get() != NULL)
176     {
177       accessor_.reset(new MemoryObjectCache::Accessor(
178                         *that.cache_, id, true /* unique */));
179       if (accessor_->IsValid())
180       {
181         const Item& item = dynamic_cast<const Item&>(accessor_->GetValue());
182         file_ = &item.GetDicom();
183         fileSize_ = item.GetMemoryUsage();
184       }
185     }
186   }
187 
188 
IsValid() const189   bool ParsedDicomCache::Accessor::IsValid() const
190   {
191     return file_ != NULL;
192   }
193 
194 
GetDicom() const195   ParsedDicomFile& ParsedDicomCache::Accessor::GetDicom() const
196   {
197     if (IsValid())
198     {
199       assert(file_ != NULL);
200       return *file_;
201     }
202     else
203     {
204       throw OrthancException(ErrorCode_BadSequenceOfCalls);
205     }
206   }
207 
208 
GetFileSize() const209   size_t ParsedDicomCache::Accessor::GetFileSize() const
210   {
211     if (IsValid())
212     {
213       return fileSize_;
214     }
215     else
216     {
217       throw OrthancException(ErrorCode_BadSequenceOfCalls);
218     }
219   }
220 }
221