Skip to content

Commit ccc0a65

Browse files
committed
Add GetSlice verb (for #110)
1 parent 4a935dd commit ccc0a65

File tree

10 files changed

+131
-46
lines changed

10 files changed

+131
-46
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# Changelog
22
Newest updates are at the top of this file.
33

4+
## August 2 2019 - v4.1.0
5+
* ibmmq - Add new verb GetSlice to mirror Get() but which returns ready-sized buffer (#110)
6+
* See updated sample amqsget.go
7+
* Some comment tidying up. Make CMQC constants constant.
8+
49
## July 30 2019 - v4.0.10
510
* ibmmq - Add error checking to some structure fields (#111)
611

ibmmq/cmqc_darwin.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ package ibmmq
3636
* <END_BUILDINFO>
3737
*/
3838

39-
var (
39+
const (
4040
MQACH_CURRENT_LENGTH int32 = 72
4141
MQACH_CURRENT_VERSION int32 = 1
4242
MQACH_LENGTH_1 int32 = 72

ibmmq/cmqc_linux_amd64.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ package ibmmq
3636
* <END_BUILDINFO>
3737
*/
3838

39-
var (
39+
const (
4040
MQACH_CURRENT_LENGTH int32 = 72
4141
MQACH_CURRENT_VERSION int32 = 1
4242
MQACH_LENGTH_1 int32 = 72

ibmmq/cmqc_linux_ppc64le.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ package ibmmq
3636
* <END_BUILDINFO>
3737
*/
3838

39-
var (
39+
const (
4040
MQACH_CURRENT_LENGTH int32 = 72
4141
MQACH_CURRENT_VERSION int32 = 1
4242
MQACH_LENGTH_1 int32 = 72

ibmmq/cmqc_linux_s390x.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ package ibmmq
3636
* <END_BUILDINFO>
3737
*/
3838

39-
var (
39+
const (
4040
MQACH_CURRENT_LENGTH int32 = 72
4141
MQACH_CURRENT_VERSION int32 = 1
4242
MQACH_LENGTH_1 int32 = 72

ibmmq/cmqc_windows.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ package ibmmq
3636
* <END_BUILDINFO>
3737
*/
3838

39-
var (
39+
const (
4040
MQACH_CURRENT_LENGTH int32 = 72
4141
MQACH_CURRENT_VERSION int32 = 1
4242
MQACH_LENGTH_1 int32 = 72

ibmmq/mqi.go

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Package ibmmq provides a wrapper to a the IBM MQ procedural interface (the MQI).
2+
Package ibmmq provides a wrapper to the IBM MQ procedural interface (the MQI).
33
44
The verbs are given mixed case names without MQ - Open instead
55
of MQOPEN etc.
@@ -9,8 +9,8 @@ constants and structures, see the MQ Knowledge Center
99
at https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.1.0/com.ibm.mq.dev.doc/q023720_.htm#q023720_
1010
1111
If an MQI call returns MQCC_FAILED or MQCC_WARNING, a custom error
12-
type is returned containing the MQCC/MQRC values as
13-
a formatted string. Use mqreturn:= err(*ibmmq.MQReturn) to access
12+
type is returned containing the MQCC/MQRC values as well as
13+
a formatted string. Use 'mqreturn:= err(*ibmmq.MQReturn)' to access
1414
the particular MQRC or MQCC values.
1515
1616
The build directives assume the default MQ installation path
@@ -607,6 +607,36 @@ The length of the retrieved message is returned.
607607
*/
608608
func (object MQObject) Get(gomd *MQMD,
609609
gogmo *MQGMO, buffer []byte) (int, error) {
610+
return object.getInternal(gomd, gogmo, buffer, false)
611+
}
612+
613+
/*
614+
GetSlice is the same as Get except that the buffer gets returned
615+
ready-sliced based on the message length instead of just returning the
616+
length. The real length is also still returned in case of truncation.
617+
*/
618+
func (object MQObject) GetSlice(gomd *MQMD,
619+
gogmo *MQGMO, buffer []byte) ([]byte, int, error) {
620+
realDatalen, err := object.getInternal(gomd, gogmo, buffer, true)
621+
622+
// The datalen will be set even if the buffer is too small - there
623+
// will be one of MQRC_TRUNCATED_MSG_ACCEPTED or _FAILED depending on the
624+
// GMO options. In any case, we return the available data along with the
625+
// error code but need to make sure that the real untruncated
626+
// message length is also returned. Also ensure we don't try to read past the
627+
// end of the buffer.
628+
datalen := realDatalen
629+
if datalen > cap(buffer) {
630+
datalen = cap(buffer)
631+
}
632+
return buffer[0:datalen], realDatalen, err
633+
}
634+
635+
/*
636+
This is the real function that calls MQGET.
637+
*/
638+
func (object MQObject) getInternal(gomd *MQMD,
639+
gogmo *MQGMO, buffer []byte, useCap bool) (int, error) {
610640

611641
var mqrc C.MQLONG
612642
var mqcc C.MQLONG
@@ -624,12 +654,23 @@ func (object MQObject) Get(gomd *MQMD,
624654
return 0, err
625655
}
626656

627-
bufflen := len(buffer)
657+
bufflen := 0
658+
if useCap {
659+
bufflen = cap(buffer)
660+
} else {
661+
bufflen = len(buffer)
662+
}
628663

629664
copyMDtoC(&mqmd, gomd)
630665
copyGMOtoC(&mqgmo, gogmo)
631666

632667
if bufflen > 0 {
668+
// There has to be something in the buffer for CGO to be able to
669+
// find its address. We know there's space backing the buffer so just
670+
// set the first byte to something.
671+
if useCap && len(buffer) == 0 {
672+
buffer = append(buffer, 0)
673+
}
633674
ptr = (C.PMQVOID)(unsafe.Pointer(&buffer[0]))
634675
} else {
635676
ptr = nil
@@ -652,7 +693,7 @@ func (object MQObject) Get(gomd *MQMD,
652693
}
653694

654695
if mqcc != C.MQCC_OK {
655-
return 0, &mqreturn
696+
return godatalen, &mqreturn
656697
}
657698

658699
return godatalen, nil
@@ -724,14 +765,14 @@ func (object MQObject) Inq(goSelectors []int32, intAttrCount int, charAttrLen in
724765
*/
725766

726767
/*
727-
* Inq is the function to inquire on an attribute of an object
728-
*
729-
* This has a much simpler API than the original implementation.
730-
* Simply pass in the list of selectors for the object
731-
* and the return value consists of a map whose elements are
732-
* a) accessed via the selector
733-
* b) varying datatype (integer, string, string array) based on the selector
734-
*/
768+
Inq is the function to inquire on an attribute of an object
769+
770+
This has a much simpler API than the original implementation.
771+
Simply pass in the list of selectors for the object
772+
and the return value consists of a map whose elements are
773+
a) accessed via the selector
774+
b) varying datatype (integer, string, string array) based on the selector
775+
*/
735776
func (object MQObject) Inq(goSelectors []int32) (map[int32]interface{}, error) {
736777
var mqrc C.MQLONG
737778
var mqcc C.MQLONG
@@ -841,18 +882,18 @@ func (object MQObject) Inq(goSelectors []int32) (map[int32]interface{}, error) {
841882
}
842883

843884
/*
844-
* The InqMap function was the migration path when the original Inq was
845-
* deprecated. It is kept here as a tempoary wrapper to the new Inq() version.
846-
*/
885+
The InqMap function was the migration path when the original Inq was
886+
deprecated. It is kept here as a temporary wrapper to the new Inq() version.
887+
*/
847888
func (object MQObject) InqMap(goSelectors []int32) (map[int32]interface{}, error) {
848889
return object.Inq(goSelectors)
849890
}
850891

851892
/*
852-
* Set is the function that wraps MQSET. The single parameter is a map whose
853-
* elements contain an MQIA/MQCA selector with either a string or an int32 for
854-
* the value.
855-
*/
893+
Set is the function that wraps MQSET. The single parameter is a map whose
894+
elements contain an MQIA/MQCA selector with either a string or an int32 for
895+
the value.
896+
*/
856897
func (object MQObject) Set(goSelectors map[int32]interface{}) error {
857898
var mqrc C.MQLONG
858899
var mqcc C.MQLONG

ibmmq/mqicb.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
package ibmmq
2+
13
/*
24
Copyright (c) IBM Corporation 2018
35
@@ -20,7 +22,6 @@
2022
/*
2123
This file deals with asynchronous delivery of MQ messages via the MQCTL/MQCB verbs.
2224
*/
23-
package ibmmq
2425

2526
/*
2627
#include <stdlib.h>

ibmmq/mqicb_c.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
package ibmmq
2+
13
/*
24
Copyright (c) IBM Corporation 2018
35
@@ -16,7 +18,6 @@
1618
Contributors:
1719
Mark Taylor - Initial Contribution
1820
*/
19-
package ibmmq
2021

2122
/*
2223
#include <stdlib.h>

samples/amqsget.go

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -139,27 +139,64 @@ func mainWithRc() int {
139139
msgAvail = false
140140
}
141141

142-
// Create a buffer for the message data. This one is large enough
143-
// for the messages put by the amqsput sample.
144-
buffer := make([]byte, 1024)
145-
146-
// Now we can try to get the message
147-
datalen, err = qObject.Get(getmqmd, gmo, buffer)
148-
149-
if err != nil {
150-
msgAvail = false
151-
fmt.Println(err)
152-
mqret := err.(*ibmmq.MQReturn)
153-
if mqret.MQRC == ibmmq.MQRC_NO_MSG_AVAILABLE {
154-
// If there's no message available, then I won't treat that as a real error as
155-
// it's an expected situation
156-
err = nil
142+
// There are now two forms of the Get verb.
143+
// The original Get() takes
144+
// a buffer and returns the length of the message. The user can then
145+
// use a slice operation to extract just the relevant data.
146+
//
147+
// The new GetSlice() returns the message data pre-sliced as an extra
148+
// return value.
149+
//
150+
// This boolean just determines which Get variation is demonstrated in the sample
151+
useGetSlice := true
152+
if useGetSlice {
153+
// Create a buffer for the message data. This one is large enough
154+
// for the messages put by the amqsput sample. Note that in this case
155+
// the make() operation is just allocating space - len(buffer)==0 initially.
156+
buffer := make([]byte, 0, 1024)
157+
158+
// Now we can try to get the message. This operation returns
159+
// a buffer that can be used directly.
160+
buffer, datalen, err = qObject.GetSlice(getmqmd, gmo, buffer)
161+
162+
if err != nil {
163+
msgAvail = false
164+
fmt.Println(err)
165+
mqret := err.(*ibmmq.MQReturn)
166+
if mqret.MQRC == ibmmq.MQRC_NO_MSG_AVAILABLE {
167+
// If there's no message available, then I won't treat that as a real error as
168+
// it's an expected situation
169+
err = nil
170+
}
171+
} else {
172+
// Assume the message is a printable string, which it will be
173+
// if it's been created by the amqsput program
174+
fmt.Printf("Got message of length %d: ", datalen)
175+
fmt.Println(strings.TrimSpace(string(buffer)))
157176
}
158177
} else {
159-
// Assume the message is a printable string, which it will be
160-
// if it's been created by the amqsput program
161-
fmt.Printf("Got message of length %d: ", datalen)
162-
fmt.Println(strings.TrimSpace(string(buffer[:datalen])))
178+
// Create a buffer for the message data. This one is large enough
179+
// for the messages put by the amqsput sample.
180+
buffer := make([]byte, 1024)
181+
182+
// Now we can try to get the message
183+
datalen, err = qObject.Get(getmqmd, gmo, buffer)
184+
185+
if err != nil {
186+
msgAvail = false
187+
fmt.Println(err)
188+
mqret := err.(*ibmmq.MQReturn)
189+
if mqret.MQRC == ibmmq.MQRC_NO_MSG_AVAILABLE {
190+
// If there's no message available, then I won't treat that as a real error as
191+
// it's an expected situation
192+
err = nil
193+
}
194+
} else {
195+
// Assume the message is a printable string, which it will be
196+
// if it's been created by the amqsput program
197+
fmt.Printf("Got message of length %d: ", datalen)
198+
fmt.Println(strings.TrimSpace(string(buffer[:datalen])))
199+
}
163200
}
164201
}
165202

0 commit comments

Comments
 (0)