mirror of
https://git.dev.opencascade.org/repos/occt.git
synced 2025-04-10 18:51:21 +03:00
0028887: TKJT - add option to cache file content
Added new auxiliary class Standard_ArrayStreamBuffer implementing std::streambuf interface for defining std::istream from allocated memory.
This commit is contained in:
parent
da67ffb75a
commit
a5460e9d98
@ -59,6 +59,8 @@
|
|||||||
#include <HLRBRep_PolyHLRToShape.hxx>
|
#include <HLRBRep_PolyHLRToShape.hxx>
|
||||||
#include <HLRBRep_PolyAlgo.hxx>
|
#include <HLRBRep_PolyAlgo.hxx>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
//=======================================================================
|
//=======================================================================
|
||||||
//function : SurfaceGenOCC26675_1
|
//function : SurfaceGenOCC26675_1
|
||||||
//purpose : Generates a surface for intersect (in corresponding
|
//purpose : Generates a surface for intersect (in corresponding
|
||||||
@ -2340,6 +2342,99 @@ static Standard_Integer OCC28829 (Draw_Interpretor&, Standard_Integer, const cha
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <NCollection_Buffer.hxx>
|
||||||
|
#include <DDocStd_DrawDocument.hxx>
|
||||||
|
#include <OSD_OpenFile.hxx>
|
||||||
|
#include <Standard_ArrayStreamBuffer.hxx>
|
||||||
|
#include <TDataStd_Name.hxx>
|
||||||
|
#include <TDocStd_Application.hxx>
|
||||||
|
|
||||||
|
#ifdef max
|
||||||
|
#undef max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static Standard_Integer OCC28887 (Draw_Interpretor&, Standard_Integer theNbArgs, const char** theArgVec)
|
||||||
|
{
|
||||||
|
if (theNbArgs < 3)
|
||||||
|
{
|
||||||
|
std::cout << "Syntax error: wrong number of arguments!\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TCollection_AsciiString aFilePath (theArgVec[1]);
|
||||||
|
const TCollection_AsciiString aName (theArgVec[2]);
|
||||||
|
Handle(NCollection_Buffer) aBuffer;
|
||||||
|
{
|
||||||
|
std::ifstream aFile;
|
||||||
|
OSD_OpenStream (aFile, aFilePath.ToCString(), std::ios::binary | std::ios::in);
|
||||||
|
if (!aFile.is_open())
|
||||||
|
{
|
||||||
|
std::cout << "Error: input file '" << aFilePath << "' cannot be read\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
aFile.seekg (0, std::ios_base::end);
|
||||||
|
const int64_t aFileLength = int64_t (aFile.tellg());
|
||||||
|
if (aFileLength > int64_t (std::numeric_limits<ptrdiff_t>::max())
|
||||||
|
|| aFileLength < 1)
|
||||||
|
{
|
||||||
|
std::cout << "Error: input file '" << aFilePath << "' is too large\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
aFile.seekg (0, std::ios_base::beg);
|
||||||
|
|
||||||
|
aBuffer = new NCollection_Buffer (NCollection_BaseAllocator::CommonBaseAllocator());
|
||||||
|
if (!aBuffer->Allocate (size_t(aFileLength)))
|
||||||
|
{
|
||||||
|
std::cout << "Error: memory allocation (" << aFileLength << ") has failed\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
aFile.read ((char* )aBuffer->ChangeData(), aBuffer->Size());
|
||||||
|
if (!aFile.good())
|
||||||
|
{
|
||||||
|
std::cout << "Error: input file '" << aFilePath << "' reading failure\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Standard_ArrayStreamBuffer aStreamBuffer ((const char* )aBuffer->ChangeData(), aBuffer->Size());
|
||||||
|
std::istream aStream (&aStreamBuffer);
|
||||||
|
// just play with seeking
|
||||||
|
aStream.seekg (0, std::ios_base::end);
|
||||||
|
aStream.seekg (0, std::ios_base::beg);
|
||||||
|
if (aFilePath.EndsWith (".brep")
|
||||||
|
|| aFilePath.EndsWith (".rle"))
|
||||||
|
{
|
||||||
|
TopoDS_Shape aShape;
|
||||||
|
BRep_Builder aBuilder;
|
||||||
|
BRepTools::Read (aShape, aStream, aBuilder);
|
||||||
|
DBRep::Set (aName.ToCString(), aShape);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Handle(TDocStd_Document) aDoc;
|
||||||
|
Handle(TDocStd_Application) anApp = DDocStd::GetApplication();
|
||||||
|
Standard_CString aNameVar = aName.ToCString();
|
||||||
|
if (DDocStd::GetDocument (aNameVar, aDoc, Standard_False))
|
||||||
|
{
|
||||||
|
std::cout << "Error: document with name " << aName << " already exists\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anApp->Open (aStream, aDoc) != PCDM_RS_OK)
|
||||||
|
{
|
||||||
|
std::cout << "Error: cannot open XDE document\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle(DDocStd_DrawDocument) aDrawDoc = new DDocStd_DrawDocument (aDoc);
|
||||||
|
TDataStd_Name::Set (aDoc->GetData()->Root(), aName.ToCString());
|
||||||
|
Draw::Set (aName.ToCString(), aDrawDoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void QABugs::Commands_20(Draw_Interpretor& theCommands) {
|
void QABugs::Commands_20(Draw_Interpretor& theCommands) {
|
||||||
const char *group = "QABugs";
|
const char *group = "QABugs";
|
||||||
|
|
||||||
@ -2363,6 +2458,10 @@ void QABugs::Commands_20(Draw_Interpretor& theCommands) {
|
|||||||
theCommands.Add("OCC28594", "OCC28594", __FILE__, OCC28594, group);
|
theCommands.Add("OCC28594", "OCC28594", __FILE__, OCC28594, group);
|
||||||
theCommands.Add("OCC28784", "OCC28784 result shape", __FILE__, OCC28784, group);
|
theCommands.Add("OCC28784", "OCC28784 result shape", __FILE__, OCC28784, group);
|
||||||
theCommands.Add("OCC28829", "OCC28829: perform invalid FPE operation", __FILE__, OCC28829, group);
|
theCommands.Add("OCC28829", "OCC28829: perform invalid FPE operation", __FILE__, OCC28829, group);
|
||||||
|
theCommands.Add("OCC28887",
|
||||||
|
"OCC28887 filePath result"
|
||||||
|
"\n\t\t: Check interface for reading BRep from memory.",
|
||||||
|
__FILE__, OCC28887, group);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ Standard.cxx
|
|||||||
Standard.hxx
|
Standard.hxx
|
||||||
Standard_AbortiveTransaction.hxx
|
Standard_AbortiveTransaction.hxx
|
||||||
Standard_Address.hxx
|
Standard_Address.hxx
|
||||||
|
Standard_ArrayStreamBuffer.hxx
|
||||||
|
Standard_ArrayStreamBuffer.cxx
|
||||||
Standard_Assert.hxx
|
Standard_Assert.hxx
|
||||||
Standard_Atomic.hxx
|
Standard_Atomic.hxx
|
||||||
Standard_Boolean.hxx
|
Standard_Boolean.hxx
|
||||||
|
183
src/Standard/Standard_ArrayStreamBuffer.cxx
Normal file
183
src/Standard/Standard_ArrayStreamBuffer.cxx
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
// Copyright (c) 2016 OPEN CASCADE SAS
|
||||||
|
//
|
||||||
|
// This file is part of Open CASCADE Technology software library.
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||||
|
// by the Free Software Foundation, with special exception defined in the file
|
||||||
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||||
|
// distribution for complete text of the license and disclaimer of any warranty.
|
||||||
|
//
|
||||||
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||||
|
// commercial license or contractual agreement.
|
||||||
|
|
||||||
|
#include <Standard_ArrayStreamBuffer.hxx>
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : Standard_ArrayStreamBuffer
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
Standard_ArrayStreamBuffer::Standard_ArrayStreamBuffer (const char* theBegin,
|
||||||
|
const size_t theSize)
|
||||||
|
: myBegin (theBegin),
|
||||||
|
myEnd (theBegin + theSize),
|
||||||
|
myCurrent(theBegin)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : ~Standard_ArrayStreamBuffer
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
Standard_ArrayStreamBuffer::~Standard_ArrayStreamBuffer()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : Init
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
void Standard_ArrayStreamBuffer::Init (const char* theBegin,
|
||||||
|
const size_t theSize)
|
||||||
|
{
|
||||||
|
myBegin = theBegin;
|
||||||
|
myEnd = theBegin + theSize;
|
||||||
|
myCurrent = theBegin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : underflow
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
Standard_ArrayStreamBuffer::int_type Standard_ArrayStreamBuffer::underflow()
|
||||||
|
{
|
||||||
|
if (myCurrent == myEnd)
|
||||||
|
{
|
||||||
|
return traits_type::eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
return traits_type::to_int_type(*myCurrent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : uflow
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
Standard_ArrayStreamBuffer::int_type Standard_ArrayStreamBuffer::uflow()
|
||||||
|
{
|
||||||
|
if (myCurrent == myEnd)
|
||||||
|
{
|
||||||
|
return traits_type::eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
return traits_type::to_int_type(*myCurrent++);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : pbackfail
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
Standard_ArrayStreamBuffer::int_type Standard_ArrayStreamBuffer::pbackfail (int_type ch)
|
||||||
|
{
|
||||||
|
if (myCurrent == myBegin
|
||||||
|
|| (ch != traits_type::eof()
|
||||||
|
&& ch != myCurrent[-1]))
|
||||||
|
{
|
||||||
|
return traits_type::eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
return traits_type::to_int_type(*--myCurrent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : showmanyc
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
std::streamsize Standard_ArrayStreamBuffer::showmanyc()
|
||||||
|
{
|
||||||
|
if (myCurrent > myEnd)
|
||||||
|
{
|
||||||
|
// assert
|
||||||
|
}
|
||||||
|
return myEnd - myCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : seekoff
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
Standard_ArrayStreamBuffer::pos_type Standard_ArrayStreamBuffer::seekoff (off_type theOff,
|
||||||
|
std::ios_base::seekdir theWay,
|
||||||
|
std::ios_base::openmode theWhich)
|
||||||
|
{
|
||||||
|
switch (theWay)
|
||||||
|
{
|
||||||
|
case std::ios_base::beg:
|
||||||
|
{
|
||||||
|
myCurrent = myBegin + theOff;
|
||||||
|
if (myCurrent >= myEnd)
|
||||||
|
{
|
||||||
|
myCurrent = myEnd;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case std::ios_base::cur:
|
||||||
|
{
|
||||||
|
myCurrent += theOff;
|
||||||
|
if (myCurrent >= myEnd)
|
||||||
|
{
|
||||||
|
myCurrent = myEnd;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case std::ios_base::end:
|
||||||
|
{
|
||||||
|
myCurrent = myEnd - theOff;
|
||||||
|
if (myCurrent < myBegin)
|
||||||
|
{
|
||||||
|
myCurrent = myBegin;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(void )theWhich;
|
||||||
|
return myCurrent - myBegin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : seekpos
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
Standard_ArrayStreamBuffer::pos_type Standard_ArrayStreamBuffer::seekpos (pos_type thePosition,
|
||||||
|
std::ios_base::openmode theWhich)
|
||||||
|
{
|
||||||
|
return seekoff (off_type(thePosition), std::ios_base::beg, theWhich);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// function : xsgetn
|
||||||
|
// purpose :
|
||||||
|
// =======================================================================
|
||||||
|
std::streamsize Standard_ArrayStreamBuffer::xsgetn (char* thePtr,
|
||||||
|
std::streamsize theCount)
|
||||||
|
{
|
||||||
|
const char* aCurrent = myCurrent + theCount;
|
||||||
|
if (aCurrent >= myEnd)
|
||||||
|
{
|
||||||
|
aCurrent = myEnd;
|
||||||
|
}
|
||||||
|
size_t aCopied = aCurrent - myCurrent;
|
||||||
|
if (aCopied == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy (thePtr, myCurrent, aCopied);
|
||||||
|
myCurrent = aCurrent;
|
||||||
|
return aCopied;
|
||||||
|
}
|
113
src/Standard/Standard_ArrayStreamBuffer.hxx
Normal file
113
src/Standard/Standard_ArrayStreamBuffer.hxx
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
// Copyright (c) 2016 OPEN CASCADE SAS
|
||||||
|
//
|
||||||
|
// This file is part of Open CASCADE Technology software library.
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||||
|
// by the Free Software Foundation, with special exception defined in the file
|
||||||
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||||
|
// distribution for complete text of the license and disclaimer of any warranty.
|
||||||
|
//
|
||||||
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||||
|
// commercial license or contractual agreement.
|
||||||
|
|
||||||
|
#ifndef _Standard_ArrayStreamBuffer_HeaderFile
|
||||||
|
#define _Standard_ArrayStreamBuffer_HeaderFile
|
||||||
|
|
||||||
|
#include <Standard_Type.hxx>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
//! Custom buffer object implementing STL interface std::streambuf for streamed reading from allocated memory block.
|
||||||
|
//! Implements minimal sub-set of methods for passing buffer to std::istream, including seek support.
|
||||||
|
//!
|
||||||
|
//! This class can be used for creating a seekable input stream in cases,
|
||||||
|
//! when the source data does not satisfies Reader requirements (non-seekable stream, compressed data)
|
||||||
|
//! or represents an in-memory resource.
|
||||||
|
//!
|
||||||
|
//! The memory itself is NOT managed by this class - it is up to the caller to ensure that passed memory pointer
|
||||||
|
//! is not released during Standard_ArrayStreamBuffer lifetime.
|
||||||
|
//!
|
||||||
|
//! Usage example:
|
||||||
|
//! @code
|
||||||
|
//! const char* theBuffer;
|
||||||
|
//! const size_t theBufferLength;
|
||||||
|
//! Standard_ArrayStreamBuffer aStreamBuffer (theBuffer, theBufferLength);
|
||||||
|
//! std::istream aStream (&aStreamBuffer);
|
||||||
|
//! TopoDS_Shape aShape;
|
||||||
|
//! BRep_Builder aBuilder;
|
||||||
|
//! BRepTools::Read (aShape, aStream, aBuilder);
|
||||||
|
//! @endcode
|
||||||
|
class Standard_ArrayStreamBuffer : public std::streambuf
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
//! Main constructor.
|
||||||
|
//! Passed pointer is stored as is (memory is NOT copied nor released with destructor).
|
||||||
|
//! @param theBegin pointer to the beggining of pre-allocated buffer
|
||||||
|
//! @param theSize length of pre-allocated buffer
|
||||||
|
Standard_EXPORT Standard_ArrayStreamBuffer (const char* theBegin,
|
||||||
|
const size_t theSize);
|
||||||
|
|
||||||
|
//! Destructor.
|
||||||
|
Standard_EXPORT virtual ~Standard_ArrayStreamBuffer();
|
||||||
|
|
||||||
|
//! (Re)-initialize the stream.
|
||||||
|
//! Passed pointer is stored as is (memory is NOT copied nor released with destructor).
|
||||||
|
//! @param theBegin pointer to the beggining of pre-allocated buffer
|
||||||
|
//! @param theSize length of pre-allocated buffer
|
||||||
|
Standard_EXPORT virtual void Init (const char* theBegin,
|
||||||
|
const size_t theSize);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
//! Get character on underflow.
|
||||||
|
//! Virtual function called by other member functions to get the current character
|
||||||
|
//! in the controlled input sequence without changing the current position.
|
||||||
|
Standard_EXPORT virtual int_type underflow() Standard_OVERRIDE;
|
||||||
|
|
||||||
|
//! Get character on underflow and advance position.
|
||||||
|
//! Virtual function called by other member functions to get the current character
|
||||||
|
//! in the controlled input sequence and then advance the position indicator to the next character.
|
||||||
|
Standard_EXPORT virtual int_type uflow() Standard_OVERRIDE;
|
||||||
|
|
||||||
|
//! Put character back in the case of backup underflow.
|
||||||
|
//! Virtual function called by other member functions to put a character back
|
||||||
|
//! into the controlled input sequence and decrease the position indicator.
|
||||||
|
Standard_EXPORT virtual int_type pbackfail (int_type ch) Standard_OVERRIDE;
|
||||||
|
|
||||||
|
//! Get number of characters available.
|
||||||
|
//! Virtual function (to be read s-how-many-c) called by other member functions
|
||||||
|
//! to get an estimate on the number of characters available in the associated input sequence.
|
||||||
|
Standard_EXPORT virtual std::streamsize showmanyc() Standard_OVERRIDE;
|
||||||
|
|
||||||
|
//! Seek to specified position.
|
||||||
|
Standard_EXPORT virtual pos_type seekoff (off_type theOff,
|
||||||
|
std::ios_base::seekdir theWay,
|
||||||
|
std::ios_base::openmode theWhich) Standard_OVERRIDE;
|
||||||
|
|
||||||
|
//! Change to specified position, according to mode.
|
||||||
|
Standard_EXPORT virtual pos_type seekpos (pos_type thePosition,
|
||||||
|
std::ios_base::openmode theWhich) Standard_OVERRIDE;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//! Read a bunch of bytes at once.
|
||||||
|
Standard_EXPORT virtual std::streamsize xsgetn (char* thePtr,
|
||||||
|
std::streamsize theCount) Standard_OVERRIDE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// copying is not allowed
|
||||||
|
Standard_ArrayStreamBuffer (const Standard_ArrayStreamBuffer& );
|
||||||
|
Standard_ArrayStreamBuffer& operator= (const Standard_ArrayStreamBuffer& );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
const char* myBegin;
|
||||||
|
const char* myEnd;
|
||||||
|
const char* myCurrent;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _Standard_ArrayStreamBuffer_HeaderFile
|
15
tests/bugs/xde/bug28887_1
Normal file
15
tests/bugs/xde/bug28887_1
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
puts "=========="
|
||||||
|
puts "OCC28887 Test case for Standard_ArrayStreamBuffer class - streaming interface for reading from allocated memory block"
|
||||||
|
puts "=========="
|
||||||
|
puts ""
|
||||||
|
|
||||||
|
pload QAcommands VISUALIZATION
|
||||||
|
OCC28887 [locate_data_file face.brep] f
|
||||||
|
|
||||||
|
vclear
|
||||||
|
vclose ALL
|
||||||
|
vinit View1
|
||||||
|
vaxo
|
||||||
|
vdisplay -dispMode 1 f
|
||||||
|
vfit
|
||||||
|
vdump $::imagedir/${::casename}.png
|
16
tests/bugs/xde/bug28887_2
Normal file
16
tests/bugs/xde/bug28887_2
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
puts "=========="
|
||||||
|
puts "OCC28887 Test case for Standard_ArrayStreamBuffer class - streaming interface for reading from allocated memory block"
|
||||||
|
puts "=========="
|
||||||
|
puts ""
|
||||||
|
|
||||||
|
pload QAcommands VISUALIZATION DCAF
|
||||||
|
catch { Close doc }
|
||||||
|
OCC28887 [locate_data_file bug23384-doc_subshapes.xbf] doc
|
||||||
|
|
||||||
|
vclear
|
||||||
|
vclose ALL
|
||||||
|
XShow doc
|
||||||
|
vaxo
|
||||||
|
vfit
|
||||||
|
vsetdispmode 1
|
||||||
|
vdump $::imagedir/${::casename}.png
|
Loading…
x
Reference in New Issue
Block a user