Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
G
golang
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
golang
Commits
e4f4ab0b
Commit
e4f4ab0b
authored
Nov 13, 2008
by
Russ Cox
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
more array methods
R=r OCL=19172 CL=19172
parent
c5f21c0d
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
139 additions
and
58 deletions
+139
-58
cast_amd64.s
src/lib/reflect/cast_amd64.s
+10
-0
gencast.sh
src/lib/reflect/gencast.sh
+1
-0
test.go
src/lib/reflect/test.go
+31
-17
value.go
src/lib/reflect/value.go
+97
-41
No files found.
src/lib/reflect/cast_amd64.s
View file @
e4f4ab0b
...
...
@@ -171,3 +171,13 @@ TEXT reflect·PtrBoolToAddr(SB),7,$-8
MOVQ AX, 16(SP)
RET
TEXT reflect·AddrToPtrRuntimeArray(SB),7,$-8
MOVQ 8(SP), AX
MOVQ AX, 16(SP)
RET
TEXT reflect·PtrRuntimeArrayToAddr(SB),7,$-8
MOVQ 8(SP), AX
MOVQ AX, 16(SP)
RET
src/lib/reflect/gencast.sh
View file @
e4f4ab0b
...
...
@@ -37,4 +37,5 @@ Float64
Float80
String
Bool
RuntimeArray
!
src/lib/reflect/test.go
View file @
e4f4ab0b
...
...
@@ -50,35 +50,35 @@ func valuedump(s, t string) {
v
:=
reflect
.
NewInitValue
(
typ
);
switch
v
.
Kind
()
{
case
reflect
.
IntKind
:
v
.
(
reflect
.
IntValue
)
.
Pu
t
(
132
);
v
.
(
reflect
.
IntValue
)
.
Se
t
(
132
);
case
reflect
.
Int8Kind
:
v
.
(
reflect
.
Int8Value
)
.
Pu
t
(
8
);
v
.
(
reflect
.
Int8Value
)
.
Se
t
(
8
);
case
reflect
.
Int16Kind
:
v
.
(
reflect
.
Int16Value
)
.
Pu
t
(
16
);
v
.
(
reflect
.
Int16Value
)
.
Se
t
(
16
);
case
reflect
.
Int32Kind
:
v
.
(
reflect
.
Int32Value
)
.
Pu
t
(
32
);
v
.
(
reflect
.
Int32Value
)
.
Se
t
(
32
);
case
reflect
.
Int64Kind
:
v
.
(
reflect
.
Int64Value
)
.
Pu
t
(
64
);
v
.
(
reflect
.
Int64Value
)
.
Se
t
(
64
);
case
reflect
.
UintKind
:
v
.
(
reflect
.
UintValue
)
.
Pu
t
(
132
);
v
.
(
reflect
.
UintValue
)
.
Se
t
(
132
);
case
reflect
.
Uint8Kind
:
v
.
(
reflect
.
Uint8Value
)
.
Pu
t
(
8
);
v
.
(
reflect
.
Uint8Value
)
.
Se
t
(
8
);
case
reflect
.
Uint16Kind
:
v
.
(
reflect
.
Uint16Value
)
.
Pu
t
(
16
);
v
.
(
reflect
.
Uint16Value
)
.
Se
t
(
16
);
case
reflect
.
Uint32Kind
:
v
.
(
reflect
.
Uint32Value
)
.
Pu
t
(
32
);
v
.
(
reflect
.
Uint32Value
)
.
Se
t
(
32
);
case
reflect
.
Uint64Kind
:
v
.
(
reflect
.
Uint64Value
)
.
Pu
t
(
64
);
v
.
(
reflect
.
Uint64Value
)
.
Se
t
(
64
);
case
reflect
.
FloatKind
:
v
.
(
reflect
.
FloatValue
)
.
Pu
t
(
3200.0
);
v
.
(
reflect
.
FloatValue
)
.
Se
t
(
3200.0
);
case
reflect
.
Float32Kind
:
v
.
(
reflect
.
Float32Value
)
.
Pu
t
(
32.0
);
v
.
(
reflect
.
Float32Value
)
.
Se
t
(
32.0
);
case
reflect
.
Float64Kind
:
v
.
(
reflect
.
Float64Value
)
.
Pu
t
(
64.0
);
v
.
(
reflect
.
Float64Value
)
.
Se
t
(
64.0
);
case
reflect
.
StringKind
:
v
.
(
reflect
.
StringValue
)
.
Pu
t
(
"stringy cheese"
);
v
.
(
reflect
.
StringValue
)
.
Se
t
(
"stringy cheese"
);
case
reflect
.
BoolKind
:
v
.
(
reflect
.
BoolValue
)
.
Pu
t
(
true
);
v
.
(
reflect
.
BoolValue
)
.
Se
t
(
true
);
}
assert
(
reflect
.
ValueToString
(
v
),
t
);
}
...
...
@@ -181,7 +181,7 @@ func main() {
var
tmp
A
=
A
{
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
};
value
:=
reflect
.
NewValue
(
&
tmp
);
assert
(
reflect
.
ValueToString
(
value
.
(
reflect
.
PtrValue
)
.
Sub
()),
"main.A·test{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}"
);
value
.
(
reflect
.
PtrValue
)
.
Sub
()
.
(
reflect
.
ArrayValue
)
.
Elem
(
4
)
.
(
reflect
.
IntValue
)
.
Pu
t
(
123
);
value
.
(
reflect
.
PtrValue
)
.
Sub
()
.
(
reflect
.
ArrayValue
)
.
Elem
(
4
)
.
(
reflect
.
IntValue
)
.
Se
t
(
123
);
assert
(
reflect
.
ValueToString
(
value
.
(
reflect
.
PtrValue
)
.
Sub
()),
"main.A·test{1, 2, 3, 4, 123, 6, 7, 8, 9, 10}"
);
}
{
...
...
@@ -190,7 +190,7 @@ func main() {
var
tmp
*
AA
=
&
tmp1
;
value
:=
reflect
.
NewValue
(
tmp
);
assert
(
reflect
.
ValueToString
(
value
.
(
reflect
.
PtrValue
)
.
Sub
()),
"main.AA·test{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}"
);
value
.
(
reflect
.
PtrValue
)
.
Sub
()
.
(
reflect
.
ArrayValue
)
.
Elem
(
4
)
.
(
reflect
.
IntValue
)
.
Pu
t
(
123
);
value
.
(
reflect
.
PtrValue
)
.
Sub
()
.
(
reflect
.
ArrayValue
)
.
Elem
(
4
)
.
(
reflect
.
IntValue
)
.
Se
t
(
123
);
assert
(
reflect
.
ValueToString
(
value
.
(
reflect
.
PtrValue
)
.
Sub
()),
"main.AA·test{1, 2, 3, 4, 123, 6, 7, 8, 9, 10}"
);
}
...
...
@@ -267,4 +267,18 @@ func main() {
st
=
t
.
(
reflect
.
StructType
);
name
,
typ
,
tag
,
offset
=
st
.
Field
(
0
);
assert
(
typ
.
String
(),
"*[]uint32"
);
t
=
reflect
.
ParseTypeString
(
""
,
"[]int32"
);
v
:=
reflect
.
NewOpenArrayValue
(
t
,
5
,
10
);
t1
:=
reflect
.
ParseTypeString
(
""
,
"*[]int32"
);
v1
:=
reflect
.
NewInitValue
(
t1
);
v1
.
(
reflect
.
PtrValue
)
.
SetSub
(
v
);
a
:=
v1
.
Interface
()
.
(
*
[]
int32
);
println
(
a
,
len
(
a
),
cap
(
a
));
for
i
:=
0
;
i
<
len
(
a
);
i
++
{
v
.
Elem
(
i
)
.
(
reflect
.
Int32Value
)
.
Set
(
int32
(
i
));
}
for
i
:=
0
;
i
<
len
(
a
);
i
++
{
println
(
a
[
i
]);
}
}
src/lib/reflect/value.go
View file @
e4f4ab0b
...
...
@@ -14,6 +14,7 @@ import (
type
Addr
uint64
// TODO: where are ptrint/intptr etc?
// Conversion functions, implemented in assembler
type
RuntimeArray
struct
func
AddrToPtrAddr
(
Addr
)
*
Addr
func
AddrToPtrInt
(
Addr
)
*
int
func
AddrToPtrInt8
(
Addr
)
*
int8
...
...
@@ -33,6 +34,8 @@ func AddrToPtrFloat64(Addr) *float64
func
AddrToPtrFloat80
(
Addr
)
*
float80
func
AddrToPtrString
(
Addr
)
*
string
func
AddrToPtrBool
(
Addr
)
*
bool
func
AddrToPtrRuntimeArray
(
Addr
)
*
RuntimeArray
func
PtrRuntimeArrayToAddr
(
*
RuntimeArray
)
Addr
export
type
Empty
interface
{}
// TODO(r): Delete when no longer needed?
...
...
@@ -92,7 +95,7 @@ func MissingCreator(typ Type, addr Addr) Value {
export
type
IntValue
interface
{
Kind
()
int
;
Get
()
int
;
Pu
t
(
int
);
Se
t
(
int
);
Type
()
Type
;
}
...
...
@@ -108,7 +111,7 @@ func (v *IntValueStruct) Get() int {
return
*
AddrToPtrInt
(
v
.
addr
)
}
func
(
v
*
IntValueStruct
)
Pu
t
(
i
int
)
{
func
(
v
*
IntValueStruct
)
Se
t
(
i
int
)
{
*
AddrToPtrInt
(
v
.
addr
)
=
i
}
...
...
@@ -117,7 +120,7 @@ func (v *IntValueStruct) Put(i int) {
export
type
Int8Value
interface
{
Kind
()
int
;
Get
()
int8
;
Pu
t
(
int8
);
Se
t
(
int8
);
Type
()
Type
;
}
...
...
@@ -133,7 +136,7 @@ func (v *Int8ValueStruct) Get() int8 {
return
*
AddrToPtrInt8
(
v
.
addr
)
}
func
(
v
*
Int8ValueStruct
)
Pu
t
(
i
int8
)
{
func
(
v
*
Int8ValueStruct
)
Se
t
(
i
int8
)
{
*
AddrToPtrInt8
(
v
.
addr
)
=
i
}
...
...
@@ -142,7 +145,7 @@ func (v *Int8ValueStruct) Put(i int8) {
export
type
Int16Value
interface
{
Kind
()
int
;
Get
()
int16
;
Pu
t
(
int16
);
Se
t
(
int16
);
Type
()
Type
;
}
...
...
@@ -158,7 +161,7 @@ func (v *Int16ValueStruct) Get() int16 {
return
*
AddrToPtrInt16
(
v
.
addr
)
}
func
(
v
*
Int16ValueStruct
)
Pu
t
(
i
int16
)
{
func
(
v
*
Int16ValueStruct
)
Se
t
(
i
int16
)
{
*
AddrToPtrInt16
(
v
.
addr
)
=
i
}
...
...
@@ -167,7 +170,7 @@ func (v *Int16ValueStruct) Put(i int16) {
export
type
Int32Value
interface
{
Kind
()
int
;
Get
()
int32
;
Pu
t
(
int32
);
Se
t
(
int32
);
Type
()
Type
;
}
...
...
@@ -183,7 +186,7 @@ func (v *Int32ValueStruct) Get() int32 {
return
*
AddrToPtrInt32
(
v
.
addr
)
}
func
(
v
*
Int32ValueStruct
)
Pu
t
(
i
int32
)
{
func
(
v
*
Int32ValueStruct
)
Se
t
(
i
int32
)
{
*
AddrToPtrInt32
(
v
.
addr
)
=
i
}
...
...
@@ -192,7 +195,7 @@ func (v *Int32ValueStruct) Put(i int32) {
export
type
Int64Value
interface
{
Kind
()
int
;
Get
()
int64
;
Pu
t
(
int64
);
Se
t
(
int64
);
Type
()
Type
;
}
...
...
@@ -208,7 +211,7 @@ func (v *Int64ValueStruct) Get() int64 {
return
*
AddrToPtrInt64
(
v
.
addr
)
}
func
(
v
*
Int64ValueStruct
)
Pu
t
(
i
int64
)
{
func
(
v
*
Int64ValueStruct
)
Se
t
(
i
int64
)
{
*
AddrToPtrInt64
(
v
.
addr
)
=
i
}
...
...
@@ -217,7 +220,7 @@ func (v *Int64ValueStruct) Put(i int64) {
export
type
UintValue
interface
{
Kind
()
int
;
Get
()
uint
;
Pu
t
(
uint
);
Se
t
(
uint
);
Type
()
Type
;
}
...
...
@@ -233,7 +236,7 @@ func (v *UintValueStruct) Get() uint {
return
*
AddrToPtrUint
(
v
.
addr
)
}
func
(
v
*
UintValueStruct
)
Pu
t
(
i
uint
)
{
func
(
v
*
UintValueStruct
)
Se
t
(
i
uint
)
{
*
AddrToPtrUint
(
v
.
addr
)
=
i
}
...
...
@@ -242,7 +245,7 @@ func (v *UintValueStruct) Put(i uint) {
export
type
Uint8Value
interface
{
Kind
()
int
;
Get
()
uint8
;
Pu
t
(
uint8
);
Se
t
(
uint8
);
Type
()
Type
;
}
...
...
@@ -258,7 +261,7 @@ func (v *Uint8ValueStruct) Get() uint8 {
return
*
AddrToPtrUint8
(
v
.
addr
)
}
func
(
v
*
Uint8ValueStruct
)
Pu
t
(
i
uint8
)
{
func
(
v
*
Uint8ValueStruct
)
Se
t
(
i
uint8
)
{
*
AddrToPtrUint8
(
v
.
addr
)
=
i
}
...
...
@@ -267,7 +270,7 @@ func (v *Uint8ValueStruct) Put(i uint8) {
export
type
Uint16Value
interface
{
Kind
()
int
;
Get
()
uint16
;
Pu
t
(
uint16
);
Se
t
(
uint16
);
Type
()
Type
;
}
...
...
@@ -283,7 +286,7 @@ func (v *Uint16ValueStruct) Get() uint16 {
return
*
AddrToPtrUint16
(
v
.
addr
)
}
func
(
v
*
Uint16ValueStruct
)
Pu
t
(
i
uint16
)
{
func
(
v
*
Uint16ValueStruct
)
Se
t
(
i
uint16
)
{
*
AddrToPtrUint16
(
v
.
addr
)
=
i
}
...
...
@@ -292,7 +295,7 @@ func (v *Uint16ValueStruct) Put(i uint16) {
export
type
Uint32Value
interface
{
Kind
()
int
;
Get
()
uint32
;
Pu
t
(
uint32
);
Se
t
(
uint32
);
Type
()
Type
;
}
...
...
@@ -308,7 +311,7 @@ func (v *Uint32ValueStruct) Get() uint32 {
return
*
AddrToPtrUint32
(
v
.
addr
)
}
func
(
v
*
Uint32ValueStruct
)
Pu
t
(
i
uint32
)
{
func
(
v
*
Uint32ValueStruct
)
Se
t
(
i
uint32
)
{
*
AddrToPtrUint32
(
v
.
addr
)
=
i
}
...
...
@@ -317,7 +320,7 @@ func (v *Uint32ValueStruct) Put(i uint32) {
export
type
Uint64Value
interface
{
Kind
()
int
;
Get
()
uint64
;
Pu
t
(
uint64
);
Se
t
(
uint64
);
Type
()
Type
;
}
...
...
@@ -333,7 +336,7 @@ func (v *Uint64ValueStruct) Get() uint64 {
return
*
AddrToPtrUint64
(
v
.
addr
)
}
func
(
v
*
Uint64ValueStruct
)
Pu
t
(
i
uint64
)
{
func
(
v
*
Uint64ValueStruct
)
Se
t
(
i
uint64
)
{
*
AddrToPtrUint64
(
v
.
addr
)
=
i
}
...
...
@@ -342,7 +345,7 @@ func (v *Uint64ValueStruct) Put(i uint64) {
export
type
FloatValue
interface
{
Kind
()
int
;
Get
()
float
;
Pu
t
(
float
);
Se
t
(
float
);
Type
()
Type
;
}
...
...
@@ -358,7 +361,7 @@ func (v *FloatValueStruct) Get() float {
return
*
AddrToPtrFloat
(
v
.
addr
)
}
func
(
v
*
FloatValueStruct
)
Pu
t
(
f
float
)
{
func
(
v
*
FloatValueStruct
)
Se
t
(
f
float
)
{
*
AddrToPtrFloat
(
v
.
addr
)
=
f
}
...
...
@@ -367,7 +370,7 @@ func (v *FloatValueStruct) Put(f float) {
export
type
Float32Value
interface
{
Kind
()
int
;
Get
()
float32
;
Pu
t
(
float32
);
Se
t
(
float32
);
Type
()
Type
;
}
...
...
@@ -383,7 +386,7 @@ func (v *Float32ValueStruct) Get() float32 {
return
*
AddrToPtrFloat32
(
v
.
addr
)
}
func
(
v
*
Float32ValueStruct
)
Pu
t
(
f
float32
)
{
func
(
v
*
Float32ValueStruct
)
Se
t
(
f
float32
)
{
*
AddrToPtrFloat32
(
v
.
addr
)
=
f
}
...
...
@@ -392,7 +395,7 @@ func (v *Float32ValueStruct) Put(f float32) {
export
type
Float64Value
interface
{
Kind
()
int
;
Get
()
float64
;
Pu
t
(
float64
);
Se
t
(
float64
);
Type
()
Type
;
}
...
...
@@ -408,7 +411,7 @@ func (v *Float64ValueStruct) Get() float64 {
return
*
AddrToPtrFloat64
(
v
.
addr
)
}
func
(
v
*
Float64ValueStruct
)
Pu
t
(
f
float64
)
{
func
(
v
*
Float64ValueStruct
)
Se
t
(
f
float64
)
{
*
AddrToPtrFloat64
(
v
.
addr
)
=
f
}
...
...
@@ -417,7 +420,7 @@ func (v *Float64ValueStruct) Put(f float64) {
export
type
Float80Value
interface
{
Kind
()
int
;
Get
()
float80
;
Pu
t
(
float80
);
Se
t
(
float80
);
Type
()
Type
;
}
...
...
@@ -436,7 +439,7 @@ func (v *Float80ValueStruct) Get() float80 {
return 0;
}
func (v *Float80ValueStruct)
Pu
t(f float80) {
func (v *Float80ValueStruct)
Se
t(f float80) {
*AddrToPtrFloat80(v.addr) = f
}
*/
...
...
@@ -446,7 +449,7 @@ func (v *Float80ValueStruct) Put(f float80) {
export
type
StringValue
interface
{
Kind
()
int
;
Get
()
string
;
Pu
t
(
string
);
Se
t
(
string
);
Type
()
Type
;
}
...
...
@@ -462,7 +465,7 @@ func (v *StringValueStruct) Get() string {
return
*
AddrToPtrString
(
v
.
addr
)
}
func
(
v
*
StringValueStruct
)
Pu
t
(
s
string
)
{
func
(
v
*
StringValueStruct
)
Se
t
(
s
string
)
{
*
AddrToPtrString
(
v
.
addr
)
=
s
}
...
...
@@ -471,7 +474,7 @@ func (v *StringValueStruct) Put(s string) {
export
type
BoolValue
interface
{
Kind
()
int
;
Get
()
bool
;
Pu
t
(
bool
);
Se
t
(
bool
);
Type
()
Type
;
}
...
...
@@ -487,7 +490,7 @@ func (v *BoolValueStruct) Get() bool {
return
*
AddrToPtrBool
(
v
.
addr
)
}
func
(
v
*
BoolValueStruct
)
Pu
t
(
b
bool
)
{
func
(
v
*
BoolValueStruct
)
Se
t
(
b
bool
)
{
*
AddrToPtrBool
(
v
.
addr
)
=
b
}
...
...
@@ -533,13 +536,9 @@ export type ArrayValue interface {
Type
()
Type
;
Open
()
bool
;
Len
()
int
;
Cap
()
int
;
Elem
(
i
int
)
Value
;
}
type
OpenArrayValueStruct
struct
{
Common
;
elemtype
Type
;
elemsize
int
;
SetLen
(
len
int
);
}
/*
...
...
@@ -547,20 +546,43 @@ type OpenArrayValueStruct struct {
struct Array {
byte* array; // actual data
uint32 nel; // number of elements
uint32 cap;
};
*/
type
RuntimeArray
struct
{
data
Addr
;
len
uint32
;
cap
uint32
;
}
type
OpenArrayValueStruct
struct
{
Common
;
elemtype
Type
;
elemsize
int
;
array
*
RuntimeArray
;
}
func
(
v
*
OpenArrayValueStruct
)
Open
()
bool
{
return
true
}
func
(
v
*
OpenArrayValueStruct
)
Len
()
int
{
return
int
(
*
AddrToPtrInt32
(
v
.
addr
+
8
));
return
int
(
v
.
array
.
len
);
}
func
(
v
*
OpenArrayValueStruct
)
Cap
()
int
{
return
int
(
v
.
array
.
cap
);
}
func
(
v
*
OpenArrayValueStruct
)
SetLen
(
len
int
)
{
if
len
>
v
.
Cap
()
{
panicln
(
"reflect: OpenArrayValueStruct.SetLen"
,
len
,
v
.
Cap
());
}
v
.
array
.
len
=
uint32
(
len
);
}
func
(
v
*
OpenArrayValueStruct
)
Elem
(
i
int
)
Value
{
base
:=
*
AddrToPtrAddr
(
v
.
addr
);
return
NewValueAddr
(
v
.
elemtype
,
base
+
Addr
(
i
*
v
.
elemsize
));
return
NewValueAddr
(
v
.
elemtype
,
v
.
array
.
data
+
Addr
(
i
*
v
.
elemsize
));
}
type
FixedArrayValueStruct
struct
{
...
...
@@ -578,6 +600,13 @@ func (v *FixedArrayValueStruct) Len() int {
return
v
.
len
}
func
(
v
*
FixedArrayValueStruct
)
Cap
()
int
{
return
v
.
len
}
func
(
v
*
FixedArrayValueStruct
)
SetLen
(
len
int
)
{
}
func
(
v
*
FixedArrayValueStruct
)
Elem
(
i
int
)
Value
{
return
NewValueAddr
(
v
.
elemtype
,
v
.
addr
+
Addr
(
i
*
v
.
elemsize
));
return
nil
...
...
@@ -592,6 +621,7 @@ func ArrayCreator(typ Type, addr Addr) Value {
v
.
typ
=
typ
;
v
.
elemtype
=
arraytype
.
Elem
();
v
.
elemsize
=
v
.
elemtype
.
Size
();
v
.
array
=
AddrToPtrRuntimeArray
(
addr
);
return
v
;
}
v
:=
new
(
FixedArrayValueStruct
);
...
...
@@ -768,6 +798,32 @@ export func NewInitValue(typ Type) Value {
return
NewValueAddr
(
typ
,
PtrUint8ToAddr
(
&
data
[
0
]));
}
/*
Run-time representation of open arrays looks like this:
struct Array {
byte* array; // actual data
uint32 nel; // number of elements
uint32 cap; // allocated number of elements
};
*/
export
func
NewOpenArrayValue
(
typ
ArrayType
,
len
,
cap
int
)
ArrayValue
{
if
!
typ
.
Open
()
{
return
nil
}
array
:=
new
(
RuntimeArray
);
size
:=
typ
.
Elem
()
.
Size
()
*
cap
;
if
size
==
0
{
size
=
1
;
}
data
:=
new
([]
uint8
,
size
);
array
.
data
=
PtrUint8ToAddr
(
&
data
[
0
]);
array
.
len
=
uint32
(
len
);
array
.
cap
=
uint32
(
cap
);
return
NewValueAddr
(
typ
,
PtrRuntimeArrayToAddr
(
array
));
}
export
func
NewValue
(
e
Empty
)
Value
{
value
,
typestring
:=
sys
.
reflect
(
e
);
p
,
ok
:=
typecache
[
typestring
];
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment